home *** CD-ROM | disk | FTP | other *** search
Wrap
// IDEAS FOR FUTURE: // - option to export only selected items // - skinning for bezier patches // - support for single-sided vs. double sided faces // TODO: // - Rend & Poses load wrong (due to skinning followed by polyFaceSmoothing) // - Hidden objects are exported ("visibility" && "lodVisibility" && (!"overrideEnabled" || "overrideVisibility")) // - Tom hall's 2nd cable jiggles // - tesselation loses skinning & material info // debug options #define ALWAYS_TESSELATE_NURBS //#define NO_INTERMEDIATE_OBJECTS #include "xportTranslator.h" #include <iostream.h> #include <assert.h> #include <conio.h> #include <math.h> #include <dxfile.h> // DirectX File Format #include <initguid.h> #include <rmxfguid.h> #include <rmxftmpl.h> #include "xskinexptemplates.h" // Additional X-file Templates #include <MDt.h> // Dt API #include "MyDt.h" // Maya API #include <maya/MSimple.h> #include <maya/MObject.h> #include <maya/MDagPath.h> #include <maya/MPlug.h> #include <maya/MSelectionList.h> #include <maya/MColor.h> #include <maya/MMatrix.h> #include <maya/MQuaternion.h> #include <maya/MEulerRotation.h> #include <maya/MTime.h> #include <maya/MIntArray.h> #include <maya/MFloatArray.h> #include <maya/MFloatVectorArray.h> #include <maya/MFloatPointArray.h> #include <maya/MPointArray.h> #include <maya/MObjectArray.h> #include <maya/MDagPathArray.h> #include <maya/MPlugArray.h> #include <maya/MAnimControl.h> #include <maya/MAnimUtil.h> #include <maya/MFnPlugin.h> #include <maya/MFnDagNode.h> #include <maya/MFnTransform.h> #include <maya/MFnIKJoint.h> #include <maya/MFnMesh.h> #include <maya/MFnNurbsSurface.h> #include <maya/MFnLambertShader.h> #include <maya/MFnBlinnShader.h> #include <maya/MFnPhongShader.h> #include <maya/MFnSkinCluster.h> #include <maya/MFnWeightGeometryFilter.h> #include <maya/MFnMatrixData.h> #include <maya/MFnSet.h> #include <maya/MItSelectionList.h> #include <maya/MItDependencyGraph.h> #include <maya/MItDependencyNodes.h> #include <maya/MItDag.h> #include <maya/MItGeometry.h> #include <maya/MItMeshPolygon.h> const GUID* g_aIds[] = {&DXFILEOBJ_XSkinMeshHeader, &DXFILEOBJ_VertexDuplicationIndices, &DXFILEOBJ_SkinWeights}; // GLOBALS CArrayTable g_Arrays; // used to keep some arrays (e.g. shape names, transforms, etc.) persistent during the export MDagPathArray g_AddedPaths; // used to save DAG path of an exported shape (so that we can later load its animation) // the following are export options DXFILEFORMAT g_FileFormat; bool g_bExportAnimation; bool g_bKeyframeAnimation; bool g_bAnimateEverything; bool g_bRelativeTexFile; bool g_bExportPatches; int g_iFrameStep; int g_iFlipU; int g_iFlipV; int g_iCount; HRESULT AddAnim ( SAnim* pAnim, LPDIRECTXFILEDATA pAnimSetObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; LPDIRECTXFILEDATA pAnimDataObject = NULL; LPDIRECTXFILEDATA pQuaternionKeyDataObject = NULL; LPDIRECTXFILEDATA pScaleKeyDataObject = NULL; LPDIRECTXFILEDATA pPositionKeyDataObject = NULL; PBYTE pbPositionKeyData = NULL; PBYTE pbScaleKeyData = NULL; PBYTE pbQuaternionKeyData = NULL; UINT iKey; // counter UINT cbQuaternionKeySize = sizeof(DWORD) // keyType + sizeof(DWORD) // nKeys + pAnim->m_nKeys * (sizeof(DWORD) + sizeof(DWORD) + 4 * sizeof(float)); // keys[nKeys] UINT cbScaleKeySize = sizeof(DWORD) // keyType + sizeof(DWORD) // nKeys + pAnim->m_nKeys * (sizeof(DWORD) + sizeof(DWORD) + 3 * sizeof(float)); // keys[nKeys] UINT cbPositionKeySize = sizeof(DWORD) // keyType + sizeof(DWORD) // nKeys + pAnim->m_nKeys * (sizeof(DWORD) + sizeof(DWORD) + 3 * sizeof(float)); // keys[nKeys] PBYTE pbPositionKeyCurr; PBYTE pbScaleKeyCurr; PBYTE pbQuaternionKeyCurr; hr = pxofSave->CreateDataObject(TID_D3DRMAnimation, NULL, NULL, 0, NULL, &pAnimDataObject); if (FAILED(hr)) { cout << "AddAnim(): Could not create pAnimDataObject." << endl; goto e_Exit; } pbPositionKeyCurr = pbPositionKeyData = new BYTE[cbPositionKeySize]; if (NULL == pbPositionKeyData) { hr = E_OUTOFMEMORY; cout << "AddAnim(): Could not allocate memory for pbPositionKeyData." << endl; goto e_Exit; } pbScaleKeyCurr = pbScaleKeyData = new BYTE[cbScaleKeySize]; if (NULL == pbScaleKeyData) { hr = E_OUTOFMEMORY; cout << "AddAnim(): Could not allocate memory for pbScaleKeyData." << endl; goto e_Exit; } pbQuaternionKeyCurr = pbQuaternionKeyData = new BYTE[cbQuaternionKeySize]; if (NULL == pbQuaternionKeyData) { hr = E_OUTOFMEMORY; cout << "AddAnim(): Could not allocate memory for pbQuaternionKeyData." << endl; goto e_Exit; } // keyType WRITE_DWORD(pbQuaternionKeyCurr, ((DWORD)0)); WRITE_DWORD(pbScaleKeyCurr, ((DWORD)1)); WRITE_DWORD(pbPositionKeyCurr, ((DWORD)2)); // nKeys WRITE_DWORD(pbQuaternionKeyCurr, ((DWORD)pAnim->m_nKeys)); WRITE_DWORD(pbScaleKeyCurr, ((DWORD)pAnim->m_nKeys)); WRITE_DWORD(pbPositionKeyCurr, ((DWORD)pAnim->m_nKeys)); // keys[nKeys] for (iKey = 0; iKey < pAnim->m_nKeys; iKey++) { // time WRITE_DWORD(pbQuaternionKeyCurr, ((DWORD)pAnim->m_aKeys[iKey].m_iFrame)); WRITE_DWORD(pbScaleKeyCurr, ((DWORD)pAnim->m_aKeys[iKey].m_iFrame)); WRITE_DWORD(pbPositionKeyCurr, ((DWORD)pAnim->m_aKeys[iKey].m_iFrame)); // nValues WRITE_DWORD(pbQuaternionKeyCurr, ((DWORD)4)); WRITE_DWORD(pbScaleKeyCurr, ((DWORD)3)); WRITE_DWORD(pbPositionKeyCurr, ((DWORD)3)); // values WRITE_FLOAT(pbQuaternionKeyCurr, pAnim->m_aKeys[iKey].m_afQuaternion[0]); WRITE_FLOAT(pbQuaternionKeyCurr, pAnim->m_aKeys[iKey].m_afQuaternion[1]); WRITE_FLOAT(pbQuaternionKeyCurr, pAnim->m_aKeys[iKey].m_afQuaternion[2]); WRITE_FLOAT(pbQuaternionKeyCurr, pAnim->m_aKeys[iKey].m_afQuaternion[3]); WRITE_FLOAT(pbScaleKeyCurr, pAnim->m_aKeys[iKey].m_afScale[0]); WRITE_FLOAT(pbScaleKeyCurr, pAnim->m_aKeys[iKey].m_afScale[1]); WRITE_FLOAT(pbScaleKeyCurr, pAnim->m_aKeys[iKey].m_afScale[2]); WRITE_FLOAT(pbPositionKeyCurr, pAnim->m_aKeys[iKey].m_afPosition[0]); WRITE_FLOAT(pbPositionKeyCurr, pAnim->m_aKeys[iKey].m_afPosition[1]); WRITE_FLOAT(pbPositionKeyCurr, pAnim->m_aKeys[iKey].m_afPosition[2]); } hr = pxofSave->CreateDataObject(TID_D3DRMAnimationKey, NULL, NULL, cbQuaternionKeySize, pbQuaternionKeyData, &pQuaternionKeyDataObject); if (FAILED(hr)) { cout << "AddAnim(): Could not create pQuaternionKeyDataObject." << endl; goto e_Exit; } hr = pxofSave->CreateDataObject(TID_D3DRMAnimationKey, NULL, NULL, cbScaleKeySize, pbScaleKeyData, &pScaleKeyDataObject); if (FAILED(hr)) { cout << "AddAnim(): Could not create pScaleKeyDataObject." << endl; goto e_Exit; } hr = pxofSave->CreateDataObject(TID_D3DRMAnimationKey, NULL, NULL, cbPositionKeySize, pbPositionKeyData, &pPositionKeyDataObject); if (FAILED(hr)) { cout << "AddAnim(): Could not create pPositionKeyDataObject." << endl; goto e_Exit; } hr = pAnimDataObject->AddDataReference(pAnim->m_szName, NULL); if (FAILED(hr)) { cout << "AddAnim(): Could not add data reference to pAnimDataObject." << endl; goto e_Exit; } hr = pAnimDataObject->AddDataObject(pQuaternionKeyDataObject); if (FAILED(hr)) { cout << "AddAnim(): Could not add pQuaternionKeyDataObject to pAnimDataObject." << endl; goto e_Exit; } hr = pAnimDataObject->AddDataObject(pScaleKeyDataObject); if (FAILED(hr)) { cout << "AddAnim(): Could not add pScaleKeyDataObject to pAnimDataObject." << endl; goto e_Exit; } hr = pAnimDataObject->AddDataObject(pPositionKeyDataObject); if (FAILED(hr)) { cout << "AddAnim(): Could not add pPositionKeyDataObject to pAnimDataObject." << endl; goto e_Exit; } hr = pAnimSetObject->AddDataObject(pAnimDataObject); if (FAILED(hr)) { cout << "AddAnim(): Could not add pAnimDataObject to pAnimSetObject." << endl; goto e_Exit; } e_Exit: delete[] pbQuaternionKeyData; delete[] pbScaleKeyData; delete[] pbPositionKeyData; if (pQuaternionKeyDataObject) pQuaternionKeyDataObject->Release(); if (pScaleKeyDataObject) pScaleKeyDataObject->Release(); if (pPositionKeyDataObject) pPositionKeyDataObject->Release(); if (pAnimDataObject) pAnimDataObject->Release(); return hr; } HRESULT LoadPolyMesh ( MDagPath& mdpTransform, MDagPath& mdpMesh, SMesh* pShape ) { HRESULT hr = S_OK; MStatus stat = MStatus::kSuccess; UINT iVert, iRep, iNorm, iUV, iGroup, iFace, iIndex, iBone, iBoneRemap; UINT cRepsMax, cBonesMax = 1; UINT iInstance; MFnMesh fnMesh; MMatrix mmWorldTransform; // geometry info MFloatPointArray mfpaPoints; MFloatVectorArray mfvaNormals; MFloatArray mfaUs, mfaVs; // material info MObjectArray maoShaders; MIntArray maiPolyShaderMap; MItMeshPolygon itPoly(MObject::kNullObj); MItDag itBones(MItDag::kDepthFirst, MFn::kJoint); // skinning info MItDependencyNodes itClusters; MFnSkinCluster fnSkin; MTransformationMatrix* amtmBonePositions = NULL; MFnWeightGeometryFilter fnCluster; bool** aabBonePointTable = NULL; // (aabBonePointTable[i][j] == true) iff bone i influences point j UINT* acBonesPerPoint = NULL; // acBonesPerPoint[j] = num bones that influence point j (used for smooth skinning) MObjectArray maoBones, maoWorldBindMatrices, maoLocalBindMatrices; MIntArray maiBoneRemap; // the following are used per bone MFnTransform fnBone; MObject moComponents, moWorldBindMatrix, moLocalBindMatrix; MPlug mpToBindPose; MFnMatrixData fnMatrix; char* szBone = NULL; char* pcBone; float* afWeights = NULL; UINT* aiPoints = NULL; bool* abBonePointArray = NULL; UINT cWeights; UINT iBoneIdx, iWeight; bool bSmoothSkinning, bRigidSkinning, bUnkownSkinning; bool bVisibility, bLodVisibility, bOverrideEnabled, bOverrideVisibility; // initialize mesh pShape->m_kType = SMesh::UNKNOWN; pShape->m_nGroups = 0; pShape->m_aGroups = NULL; pShape->m_nPoints = 0; pShape->m_aPoints = NULL; pShape->m_nVertexColors = 0; pShape->m_aVertexColors = NULL; pShape->m_nNormals = 0; pShape->m_aNormals = NULL; pShape->m_nUVs = 0; pShape->m_aUVs = NULL; pShape->m_nReps = 0; pShape->m_aReps = NULL; pShape->m_nFaces = 0; pShape->m_aFaces = NULL; pShape->m_nFaceIndices = 0; pShape->m_nBones = 0; pShape->m_aBones = NULL; pShape->m_nMaxBonesPerFace = 0; pShape->m_nMaxBonesPerPoint = 0; mmWorldTransform = mdpTransform.inclusiveMatrix(); // load the mesh's world transform (needed if skinning info is found) if (!mdpMesh.hasFn(MFn::kMesh)) { cout << "LoadPolyMesh(): Input path was not a mesh as expected. Aborting..." << endl; goto e_Exit; } stat = fnMesh.setObject(mdpMesh); if (!stat) { cout << "LoadPolyMesh(): Aborting because object could not be read as mesh." << endl; goto e_Exit; } // check if mesh is visible bVisibility = true; bLodVisibility = true; bOverrideEnabled = false; bOverrideVisibility = true; do // ONCE { MPlug mpVisibility, mpLodVisibility, mpOverrideEnabled, mpOverrideVisibility; mpVisibility = fnMesh.findPlug("visibility", &stat); if (!stat) break; stat = mpVisibility.getValue(bVisibility); if (!stat) break; mpLodVisibility = fnMesh.findPlug("lodVisibility", &stat); if (!stat) break; stat = mpLodVisibility.getValue(bLodVisibility); if (!stat) break; mpOverrideEnabled = fnMesh.findPlug("overrideEnabled", &stat); if (!stat) break; stat = mpOverrideEnabled.getValue(bOverrideEnabled); if (!stat) break; mpOverrideVisibility = fnMesh.findPlug("overrideVisibility", &stat); if (!stat) break; stat = mpOverrideVisibility.getValue(bOverrideVisibility); if (!stat) break; } while (false); // if mesh is not visible then skip it if (!(bVisibility && bLodVisibility && (!bOverrideEnabled || bOverrideVisibility))) { cout << "Skipping mesh \"" << fnMesh.name().asChar() << "\" because it is invisible..." << endl; goto e_Exit; } cout << "\t\tLoading mesh \"" << fnMesh.name().asChar() << "\"..." << endl; // get instance number of mesh (if mesh is uninstanced then default is 0) iInstance = 0; if (mdpMesh.isInstanced()) { iInstance = mdpMesh.instanceNumber(); cout << "LoadPolyMesh(): This mesh, \"" << mdpMesh.fullPathName() << "\" is instanced (instance num " << iInstance << "); instanced meshes haven't been tested yet." << endl; } pShape->m_nPoints = (UINT)fnMesh.numVertices(); // point count if (pShape->m_nPoints > 0) { pShape->m_aPoints = new DtVec3f[pShape->m_nPoints]; if (pShape->m_aPoints == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for vertices." << endl; goto e_Exit; } } else { cout << "LoadPolyMesh(): Aborting because no vertices were found." << endl; goto e_Exit; } pShape->m_nNormals = (UINT)fnMesh.numNormals(); // normal count if (pShape->m_nNormals > 0) { pShape->m_aNormals = new DtVec3f[pShape->m_nNormals]; if (NULL == pShape->m_aNormals) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for normals." << endl; goto e_Exit; } } pShape->m_nUVs = (UINT)fnMesh.numUVs(); // uv count if (pShape->m_nUVs > 0) { pShape->m_aUVs = new DtVec2f[pShape->m_nUVs]; if (NULL == pShape->m_aUVs) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for tex-coords." << endl; goto e_Exit; } } // MATERIALS stat = fnMesh.getConnectedShaders(iInstance, maoShaders, maiPolyShaderMap); if (!stat) { cout << "LoadPolyMesh(): Aborting because shaders could not be found." << endl; goto e_Exit; } pShape->m_nGroups = maoShaders.length(); if (pShape->m_nGroups == 0) goto e_ExitMaterials; pShape->m_aGroups = new SGroup[pShape->m_nGroups]; if (pShape->m_aGroups == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for materials." << endl; goto e_Exit; } for (iGroup = 0; iGroup < pShape->m_nGroups; iGroup++) { // load default values into group pShape->m_aGroups[iGroup].m_fDiffuseRed = 1.0f; pShape->m_aGroups[iGroup].m_fDiffuseGreen = 1.0f; pShape->m_aGroups[iGroup].m_fDiffuseBlue = 1.0f; pShape->m_aGroups[iGroup].m_fEmissiveRed = 0.0f; pShape->m_aGroups[iGroup].m_fEmissiveGreen = 0.0f; pShape->m_aGroups[iGroup].m_fEmissiveBlue = 0.0f; pShape->m_aGroups[iGroup].m_fSpecularRed = 1.0f; pShape->m_aGroups[iGroup].m_fSpecularGreen = 1.0f; pShape->m_aGroups[iGroup].m_fSpecularBlue = 1.0f; pShape->m_aGroups[iGroup].m_fTransparency = 0.0f; pShape->m_aGroups[iGroup].m_fShininess = 1.0f; pShape->m_aGroups[iGroup].m_szName = NULL; pShape->m_aGroups[iGroup].m_szTextureFile = NULL; MPlug mpShader = MFnDependencyNode(maoShaders[iGroup]).findPlug("surfaceShader", &stat); if (!stat || mpShader.isNull()) { cout << "WARNING: Using default material because shader was not a surfaceShader." << endl; continue; } else { MPlugArray aPlugs; MFnDependencyNode fnShaderNode; stat = fnShaderNode.setObject(mpShader.node()); if (stat) // on success { // get material name pShape->m_aGroups[iGroup].m_szName = new char[1 + fnShaderNode.name().length()]; if (NULL == pShape->m_aGroups[iGroup].m_szName) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate material name." << endl; goto e_Exit; } strcpy(pShape->m_aGroups[iGroup].m_szName, fnShaderNode.name().asChar()); for (pcBone = pShape->m_aGroups[iGroup].m_szName; *pcBone != '\0'; pcBone++) { // replace '|' and ' ' characters by '_' in the maya partial pathname if (*pcBone == ' ' || *pcBone == '|') *pcBone = '_'; } g_Arrays.Add(STRING, pShape->m_aGroups[iGroup].m_szName); } mpShader.connectedTo(aPlugs, true, false, &stat); if (!stat || aPlugs.length() != 1) { cout << "WARNING: Using default material because there was an error getting the shader." << endl; continue; } // default values MColor mcDiffuse(pShape->m_aGroups[iGroup].m_fDiffuseRed, pShape->m_aGroups[iGroup].m_fDiffuseGreen, pShape->m_aGroups[iGroup].m_fDiffuseBlue); MColor mcSpecular(pShape->m_aGroups[iGroup].m_fSpecularRed, pShape->m_aGroups[iGroup].m_fSpecularGreen, pShape->m_aGroups[iGroup].m_fSpecularBlue); MColor mcEmissive(pShape->m_aGroups[iGroup].m_fEmissiveRed, pShape->m_aGroups[iGroup].m_fEmissiveGreen, pShape->m_aGroups[iGroup].m_fEmissiveBlue); MColor mcTransparency(pShape->m_aGroups[iGroup].m_fTransparency, pShape->m_aGroups[iGroup].m_fTransparency, pShape->m_aGroups[iGroup].m_fTransparency); float fDiffuseCoeff = 1.0f; switch (aPlugs[0].node().apiType()) { case MFn::kLambert: { MFnLambertShader fnShader(aPlugs[0].node(), &stat); if (!stat) break; fDiffuseCoeff = fnShader.diffuseCoeff(); mcDiffuse = fnShader.color() * fDiffuseCoeff; mcEmissive = fnShader.incandescence() * fnShader.glowIntensity(); mcSpecular = mcDiffuse; // set specular color to the diffuse color mcTransparency = fnShader.transparency(); pShape->m_aGroups[iGroup].m_fShininess = 0.0f; break; } case MFn::kBlinn: { MFnBlinnShader fnShader(aPlugs[0].node(), &stat); if (!stat) break; fDiffuseCoeff = fnShader.diffuseCoeff(); mcDiffuse = fnShader.color() * fDiffuseCoeff; mcEmissive = fnShader.incandescence() * fnShader.glowIntensity(); mcSpecular = fnShader.specularColor(); mcTransparency = fnShader.transparency(); pShape->m_aGroups[iGroup].m_fShininess = fnShader.reflectivity(); // TODO: I'm not at all sure about this... break; } case MFn::kPhong: { MFnPhongShader fnShader(aPlugs[0].node(), &stat); if (!stat) break; fDiffuseCoeff = fnShader.diffuseCoeff(); mcDiffuse = fnShader.color() * fDiffuseCoeff; mcEmissive = fnShader.incandescence() * fnShader.glowIntensity(); mcSpecular = fnShader.specularColor(); mcTransparency = fnShader.transparency(); pShape->m_aGroups[iGroup].m_fShininess = fnShader.reflectivity(); // TODO: I'm not at all sure about this... break; } default: { MFnDependencyNode fnNode(aPlugs[0].node(), &stat); if (!stat) break; MPlug mpDiffuse = fnNode.findPlug("outColor"); if (stat && mpDiffuse.numElements() >= 3) { mpDiffuse[0].getValue(mcDiffuse.r); mpDiffuse[1].getValue(mcDiffuse.g); mpDiffuse[2].getValue(mcDiffuse.b); mcSpecular = mcDiffuse; } MPlug mpEmissive = fnNode.findPlug("outGlowColor", &stat); if (stat && mpEmissive.numElements() >= 3) { mpEmissive[0].getValue(mcDiffuse.r); mpEmissive[1].getValue(mcDiffuse.g); mpEmissive[2].getValue(mcDiffuse.b); } MPlug mpTransparency = fnNode.findPlug("outTransparency", &stat); if (stat && mpTransparency.numElements() >= 3) { mpTransparency[0].getValue(mcDiffuse.r); mpTransparency[1].getValue(mcDiffuse.g); mpTransparency[2].getValue(mcDiffuse.b); } break; } } pShape->m_aGroups[iGroup].m_fDiffuseRed = mcDiffuse.r; pShape->m_aGroups[iGroup].m_fDiffuseGreen = mcDiffuse.g; pShape->m_aGroups[iGroup].m_fDiffuseBlue = mcDiffuse.b; pShape->m_aGroups[iGroup].m_fSpecularRed = mcSpecular.r; pShape->m_aGroups[iGroup].m_fSpecularGreen = mcSpecular.g; pShape->m_aGroups[iGroup].m_fSpecularBlue = mcSpecular.b; pShape->m_aGroups[iGroup].m_fEmissiveRed = mcEmissive.r; pShape->m_aGroups[iGroup].m_fEmissiveGreen = mcEmissive.g; pShape->m_aGroups[iGroup].m_fEmissiveBlue = mcEmissive.b; // TODO: not sure if this is the best way to get transparency // take root mean square of the components of mcolTransparency mcTransparency *= mcTransparency; pShape->m_aGroups[iGroup].m_fTransparency = (float)sqrt((mcTransparency.r + mcTransparency.g + mcTransparency.b) / 3); // find texture file info if available MPlug mpColor = MFnDependencyNode(aPlugs[0].node()).findPlug("color", &stat); if (!stat) continue; MItDependencyGraph dgIt(mpColor, MFn::kFileTexture, MItDependencyGraph::kUpstream, MItDependencyGraph::kBreadthFirst, MItDependencyGraph::kNodeLevel, &stat); if (!stat) continue; dgIt.disablePruningOnFilter(); if (dgIt.isDone()) continue; // no texture file node was found, so just continue. // get the texture file name MString msTextureFile; MFnDependencyNode(dgIt.thisNode()).findPlug("fileTextureName").getValue(msTextureFile); pShape->m_aGroups[iGroup].m_szTextureFile = NULL; if (g_bRelativeTexFile) { // use a trick to get the relative file name without too much hassle MFileObject mFile; mFile.setFullName(msTextureFile); pShape->m_aGroups[iGroup].m_szTextureFile = new char[mFile.name().length() + 1]; if (pShape->m_aGroups[iGroup].m_szTextureFile == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Not enough memory for texture file name." << endl; goto e_Exit; } strcpy(pShape->m_aGroups[iGroup].m_szTextureFile, mFile.name().asChar()); } else { // directly use the texture file name pShape->m_aGroups[iGroup].m_szTextureFile = new char[msTextureFile.length() + 1]; if (pShape->m_aGroups[iGroup].m_szTextureFile == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Not enough memory for texture file name." << endl; goto e_Exit; } strcpy(pShape->m_aGroups[iGroup].m_szTextureFile, msTextureFile.asChar()); } g_Arrays.Add(STRING, pShape->m_aGroups[iGroup].m_szTextureFile); // TODO: in case of texture, use diffuse coeff as diffuse color pShape->m_aGroups[iGroup].m_fDiffuseRed = fDiffuseCoeff; pShape->m_aGroups[iGroup].m_fDiffuseGreen = fDiffuseCoeff; pShape->m_aGroups[iGroup].m_fDiffuseBlue = fDiffuseCoeff; } } e_ExitMaterials: // FACES & REPS // initialize reps pShape->m_nReps = pShape->m_nPoints; cRepsMax = pShape->m_nReps; // NOTE: cRepsMax > 0 (because earlier we returned if m_nPoints == 0) pShape->m_aReps = NULL; pShape->m_aReps = new SRep[cRepsMax]; if (pShape->m_aReps == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for point reps." << endl; goto e_Exit; } for (iRep = 0; iRep < pShape->m_nReps; iRep++) { pShape->m_aReps[iRep].m_iUV = (UINT)(-1); // signifies "unvisited" pShape->m_aReps[iRep].m_iNorm = (UINT)(-1); // signifies "unvisited" pShape->m_aReps[iRep].m_iNext = iRep; pShape->m_aReps[iRep].m_iFirst = iRep; pShape->m_aReps[iRep].m_nReps = 0; } // load faces pShape->m_nFaces = (UINT)fnMesh.numPolygons(); if (pShape->m_nFaces == 0) { cout << "WARNING: Aborting because mesh contains no faces." << endl; goto e_Exit; } if (pShape->m_nGroups > 0 && maiPolyShaderMap.length() != pShape->m_nFaces) { cout << "WARNING: Aborting because polygon/shader map length is not equal to number of faces." << endl; goto e_Exit; } pShape->m_aFaces = new SFace[pShape->m_nFaces]; if (pShape->m_aFaces == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for faces." << endl; goto e_Exit; } pShape->m_nFaceIndices = 0; stat = itPoly.reset(fnMesh.object()); if (!stat) { cout << "WARNING: Aborting because polygon iterator could not be initialized." << endl; goto e_Exit; } for (iFace = 0; !itPoly.isDone(); iFace++, itPoly.next()) { if (pShape->m_nGroups > 0) pShape->m_aFaces[iFace].m_iGroup = maiPolyShaderMap[iFace]; else pShape->m_aFaces[iFace].m_iGroup = (UINT)(-1); if (itPoly.polygonVertexCount() == 0) goto e_Exit; pShape->m_aFaces[iFace].m_nIndices = itPoly.polygonVertexCount(); pShape->m_aFaces[iFace].m_aIndices = NULL; pShape->m_aFaces[iFace].m_aIndices = new UINT[pShape->m_aFaces[iFace].m_nIndices]; if (pShape->m_aFaces[iFace].m_aIndices == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for face " << iFace << "'s indices." << endl; goto e_Exit; } for (iIndex = 0; iIndex < pShape->m_aFaces[iFace].m_nIndices; iIndex++) { int iUV; bool bFound = false; iVert = itPoly.vertexIndex(iIndex, &stat); if (!stat) { hr = E_FAIL; cout << "LoadPolyMesh(): Could not load face " << iFace << "'s vertex number " << iVert << endl; goto e_Exit; } if (pShape->m_nNormals > 0) { iNorm = itPoly.normalIndex(iIndex, &stat); if (!stat) { iNorm = (UINT)(-1); cout << "WARNING: Using default normal because there was an error in retrieving it." << endl; } } else { iNorm = (UINT)(-1); } if (pShape->m_nUVs > 0) { stat = itPoly.getUVIndex(iIndex, iUV); if (!stat) { iUV = (UINT)(-1); cout << "WARNING: Using default UV because there was an error in retrieving it." << endl; } } else { iUV = (UINT)(-1); } if (pShape->m_aReps[iVert].m_nReps == 0) { // first time through this rep pShape->m_aReps[iVert].m_iUV = (UINT)iUV; pShape->m_aReps[iVert].m_iNorm = iNorm; pShape->m_aReps[iVert].m_nReps = 1; pShape->m_aFaces[iFace].m_aIndices[iIndex] = iVert; // update face indices } else { UINT iPrevRep; iRep = iVert; do { if ((UINT)iUV == pShape->m_aReps[iRep].m_iUV && iNorm == pShape->m_aReps[iRep].m_iNorm) { bFound = true; pShape->m_aFaces[iFace].m_aIndices[iIndex] = iRep; // update face indices } iPrevRep = iRep; iRep = pShape->m_aReps[iRep].m_iNext; } while (!bFound && iRep != pShape->m_aReps[iRep].m_iFirst); if (!bFound) { // NOTE: iRep == iVert == pShape->m_aReps[iRep].m_iFirst (HINT: see the condition in the do..while loop above) // append new rep if (pShape->m_nReps >= cRepsMax) { // double array size cRepsMax += cRepsMax; SRep* rgNewReps; rgNewReps = NULL; rgNewReps = new SRep[cRepsMax]; if (rgNewReps == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for new rep array." << endl; goto e_Exit; } memcpy(rgNewReps, pShape->m_aReps, pShape->m_nReps * sizeof(SRep)); delete[] pShape->m_aReps; pShape->m_aReps = rgNewReps; } // create a new rep at the end of the array pShape->m_aReps[pShape->m_nReps].m_iUV = iUV; pShape->m_aReps[pShape->m_nReps].m_iNorm = iNorm; pShape->m_aReps[pShape->m_nReps].m_iFirst = iRep; pShape->m_aReps[pShape->m_nReps].m_iNext = iRep; // link the new rep to the first rep pShape->m_aReps[iPrevRep].m_iNext = pShape->m_nReps; // link the last rep to the new rep pShape->m_aReps[iRep].m_nReps++; // increment the rep count at the first rep pShape->m_aFaces[iFace].m_aIndices[iIndex] = pShape->m_nReps; // update face indices pShape->m_nReps++; // increment the total rep count } // if !bFound } } // for loop through indices pShape->m_nFaceIndices += pShape->m_aFaces[iFace].m_nIndices; } // for loop through faces if ((UINT)fnMesh.numPolygons() != iFace) { cout << "WARNING: Aborting because of mismatch in face count." << endl; goto e_Exit; } // SKINNING acBonesPerPoint = new UINT[pShape->m_nPoints]; if (acBonesPerPoint == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for bones-per-point counts." << endl; goto e_Exit; } memset(acBonesPerPoint, 0, pShape->m_nPoints * sizeof(UINT)); // zero out the bone counts pShape->m_aBones = new SBone[cBonesMax]; // NOTE: cBonesMax is initialized to 10 if (pShape->m_aBones == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for bones." << endl; goto e_Exit; } aabBonePointTable = new bool*[cBonesMax]; if (aabBonePointTable == NULL) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for point/bone table." << endl; goto e_Exit; } memset(aabBonePointTable, 0, cBonesMax * sizeof(bool*)); // NULLify the array // SMOOTH SKINNING stat = itClusters.reset(MFn::kSkinClusterFilter); if (!stat) { cout << "WARNING: Could not search skin clusters." << endl; goto e_ExitSmoothSkinning; } for (bSmoothSkinning = false; !itClusters.isDone(); itClusters.next()) { MDagPathArray dagBonePaths; stat = fnSkin.setObject(itClusters.item()); if (!stat) { cout << "WARNING: Ignoring this skin cluster because couldn't read the object." << endl; continue; } fnSkin.indexForOutputShape(fnMesh.object(), &stat); if (!stat) continue; // our mesh is not an output shape for this skin cluster so skip this cluster if (!bSmoothSkinning) { cout << "\t\t\tSmooth skinning:"; bSmoothSkinning = true; } // bone DAG paths fnSkin.influenceObjects(dagBonePaths, &stat); if (!stat) { cout << "WARNING: Ignoring this skin cluster because could not get influence objects." << endl; continue; } for (iBone = 0; iBone < dagBonePaths.length(); iBone++) { MFloatArray Weights; MSelectionList SelectionList; MDagPath dagPath; cout << " " << dagBonePaths[iBone].partialPathName(); // load affected points & weights stat = fnSkin.getPointsAffectedByInfluence(dagBonePaths[iBone], SelectionList, Weights); if (!stat) { cout << "WARNING: Ignoring bone because skin weights could not be obtained." << endl; continue; } cWeights = Weights.length(); if (0 == cWeights) continue; // no influences so ignore this bone if (1 != SelectionList.length()) { // unexpected selection list size cout << "WARNING: Ignoring bone because length of selection list is not 1." << endl; continue; } stat = SelectionList.getDagPath(0, dagPath, moComponents); if (!stat) { cout << "WARNING: Ignoring bone because could not read DAG path from selection list." << endl; continue; } if (!(dagPath == mdpMesh)) { // bone does not influence this mesh cout << "WARNING: Ignoring bone because unexpected affected mesh encountered." << endl; continue; } if (!dagBonePaths[iBone].hasFn(MFn::kTransform)) { cout << "WARNING: Ignoring bone because it is not a transform." << endl; continue; } stat = fnBone.setObject(dagBonePaths[iBone]); if (!stat) { cout << "WARNING: Ignoring bone because could not create function set." << endl; continue; } // BIND POSE moWorldBindMatrix = MObject::kNullObj; moLocalBindMatrix = MObject::kNullObj; mpToBindPose = fnBone.findPlug("bindPose", &stat); if (stat) // success { stat = mpToBindPose.getValue(moWorldBindMatrix); if (!stat) { cout << "WARNING: Ignoring bone. (Could not get 'bindPose' plug value.)" << endl; continue; } } else // failure { mpToBindPose = fnBone.findPlug("message", &stat); if (!stat) { cout << "WARNING: Ignoring bone. (Could not find 'message' plug.)" << endl; continue; } } // attempt to read local transform (preferable), and possibly world transform (if it wasn't read before) do { MPlug mpDagPose; MFnDependencyNode fnDagPose; MPlugArray mapConnections; MObject moAttribWM, moAttribXM; mpToBindPose.connectedTo(mapConnections, false, true, &stat); if (!stat) break; if (mapConnections.length() == 0) { stat = MS::kFailure; break; } mpDagPose = mapConnections[0]; // TODO: search through all connections instead of simply picking the first one. stat = fnDagPose.setObject(mpDagPose.node()); if (!stat) break; // world bind pose matrix (overwrites moWorldBindMatrix if read previously) while (moWorldBindMatrix.isNull()) { moAttribWM = fnDagPose.attribute("worldMatrix", &stat); if (!stat) break; MPlug mpWorldMatrix(mpDagPose.node(), moAttribWM); stat = mpWorldMatrix.selectAncestorLogicalIndex(mpDagPose.logicalIndex(), moAttribWM); if (!stat) break; stat = mpWorldMatrix.getValue(moWorldBindMatrix); if (!stat) break; } // local bind pose matrix while (moLocalBindMatrix.isNull()) { moAttribXM = fnDagPose.attribute("xformMatrix", &stat); if (!stat) break; MPlug mpXformMatrix(mpDagPose.node(), moAttribXM); stat = mpXformMatrix.selectAncestorLogicalIndex(mpDagPose.logicalIndex(), moAttribXM); if (!stat) break; stat = mpXformMatrix.getValue(moLocalBindMatrix); if (!stat) break; } break; } while (false); if (moWorldBindMatrix.isNull() && moLocalBindMatrix.isNull()) { cout << "WARNING: Ignoring bone. (Neither local nor world bind matrices could be obtained.)" << endl; continue; } // get bone name delete[] szBone; szBone = NULL; szBone = new char[1 + dagBonePaths[iBone].partialPathName().length()]; if (NULL == szBone) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate bone name." << endl; goto e_Exit; } strcpy(szBone, dagBonePaths[iBone].partialPathName().asChar()); for (pcBone = szBone; *pcBone != '\0'; pcBone++) { // replace '|' and ' ' characters by '_' in the maya partial pathname if (*pcBone == ' ' || *pcBone == '|') *pcBone = '_'; } iBoneIdx = pShape->m_nBones; // add bone if (iBoneIdx == cBonesMax) { // double array size SBone* aBones = NULL; bool** aabBPT = NULL; cBonesMax *= 2; aBones = new SBone[cBonesMax]; if (NULL == aBones) { cout << "LoadPolyMesh(): Could not allocate bones." << endl; goto e_Exit; } memcpy(aBones, pShape->m_aBones, pShape->m_nBones * sizeof(SBone)); aabBPT = new bool*[cBonesMax]; if (NULL == aabBPT) { cout << "LoadPolyMesh(): Could not allocate bones." << endl; goto e_Exit; } memset(aabBPT, 0, cBonesMax * sizeof(bool*)); // NULLify the table memcpy(aabBPT, aabBonePointTable, pShape->m_nBones * sizeof(bool*)); delete[] pShape->m_aBones; delete[] aabBonePointTable; pShape->m_aBones = aBones; aabBonePointTable = aabBPT; } if (iBoneIdx == pShape->m_nBones) { aabBonePointTable[iBoneIdx] = new bool[pShape->m_nPoints]; if (NULL == aabBonePointTable) { cout << "LoadPolyMesh(): Could not allocate new column for bone point table." << endl; goto e_Exit; } memset(aabBonePointTable[iBoneIdx], 0, pShape->m_nPoints * sizeof(bool)); // falsify entries pShape->m_aBones[iBoneIdx].m_szName = szBone; g_Arrays.Add(STRING, pShape->m_aBones[iBoneIdx].m_szName); szBone = NULL; maoBones.append(MObject::kNullObj); maoWorldBindMatrices.append(MObject::kNullObj); maoLocalBindMatrices.append(MObject::kNullObj); pShape->m_nBones++; } // save object & bind position maoBones[iBoneIdx] = fnBone.object(); maoWorldBindMatrices[iBoneIdx] = moWorldBindMatrix; maoLocalBindMatrices[iBoneIdx] = moLocalBindMatrix; // get the points & weights afWeights = NULL; afWeights = new float[pShape->m_aBones[iBoneIdx].m_nWeights + cWeights]; if (NULL == afWeights) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for bone weights." << endl; goto e_Exit; } memcpy (afWeights, pShape->m_aBones[iBoneIdx].m_afWeights, pShape->m_aBones[iBoneIdx].m_nWeights * sizeof(float)); delete[] pShape->m_aBones[iBoneIdx].m_afWeights; pShape->m_aBones[iBoneIdx].m_afWeights = afWeights; afWeights = NULL; aiPoints = NULL; aiPoints = new UINT[pShape->m_aBones[iBoneIdx].m_nWeights + cWeights]; if (NULL == aiPoints) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for bone influenced points." << endl; goto e_Exit; } memcpy (aiPoints, pShape->m_aBones[iBoneIdx].m_aiPoints, pShape->m_aBones[iBoneIdx].m_nWeights * sizeof(UINT)); delete[] pShape->m_aBones[iBoneIdx].m_aiPoints; pShape->m_aBones[iBoneIdx].m_aiPoints = aiPoints; aiPoints = NULL; MItGeometry itPoints(dagPath, moComponents); if ((UINT)itPoints.count() != cWeights) { hr = E_FAIL; cout << "WARNING: Ignoring bone because number of points differed from number of weights." << endl; goto e_Exit; } for (iWeight = 0; !itPoints.isDone(); itPoints.next(), iWeight++) { UINT iIndex = itPoints.index(); if (iIndex >= pShape->m_nPoints) { cout << "WARNING: Bone affects invalid index - ignoring..." << endl; continue; } if (aabBonePointTable[iBoneIdx][iIndex]) { cout << "WARNING: Ignoring the effect of this bone on this point because the point was previously affected by this bone before." << endl; continue; } pShape->m_aBones[iBoneIdx].m_aiPoints[pShape->m_aBones[iBoneIdx].m_nWeights] = iIndex; pShape->m_aBones[iBoneIdx].m_afWeights[pShape->m_aBones[iBoneIdx].m_nWeights] = Weights[iWeight]; pShape->m_aBones[iBoneIdx].m_nWeights++; pShape->m_aBones[iBoneIdx].m_nReps += pShape->m_aReps[iIndex].m_nReps; acBonesPerPoint[iIndex]++; aabBonePointTable[iBoneIdx][iIndex] = true; } // loop through weights } // loop through bones } // loop through skin clusters e_ExitSmoothSkinning: if (bSmoothSkinning) cout << endl; // RIGID SKINNING stat = itClusters.reset(MFn::kJointCluster); if (!stat) { cout << "WARNING: Could not search joint clusters." << endl; goto e_ExitRigidSkinning; } for (bRigidSkinning = false; !itClusters.isDone(); itClusters.next()) { MFnDagNode fnDagNode; UINT iSet; MObject moJointAttrib, moDeformerSet; MFnDependencyNode fnNode; MFnSet fnSet; MPlugArray aPlugs; MSelectionList SetList; MDagPath dagPath; MFloatArray Weights; stat = fnCluster.setObject(itClusters.item()); if (!stat) { cout << "WARNING: Ignoring this joint cluster because couldn't read the object." << endl; continue; } fnCluster.indexForOutputShape(fnMesh.object(), &stat); if (!stat) continue; // our mesh is not an output shape for this skin cluster so skip this cluster // get joint if (!itClusters.item().hasFn(MFn::kDependencyNode)) { cout << "WARNING: Ignoring joint cluster because object was not a dependency node." << endl; continue; } stat = fnNode.setObject(itClusters.item()); if (!stat) { cout << "WARNING: Ignoring joint cluster because dependency node could not be read." << endl; continue; } moJointAttrib = fnNode.attribute("matrix", &stat); if (!stat) { cout << "WARNING: Ignoring joint cluster because could not get 'matrix' attribute." << endl; continue; } if (!MPlug(fnNode.object(), moJointAttrib).connectedTo(aPlugs, true, false, &stat) || !stat || aPlugs.length() != 1) { cout << "WARNING: Ignoring joint cluster because there was a problem getting connections to plug." << endl; continue; } if (aPlugs[0].node().isNull() || !aPlugs[0].node().hasFn(MFn::kTransform)) { cout << "WARNING: Ignoring joint cluster because no transform attached to this cluster." << endl; continue; } stat = fnBone.setObject(aPlugs[0].node()); if (!stat) { cout << "WARNING: Ignoring joint cluster because could not read joint object." << endl; continue; } // get deformed objects moDeformerSet = fnCluster.deformerSet(&stat); if (!stat) { cout << "WARNING: Ignoring joint cluster because could not get deformer set object." << endl; continue; } stat = fnSet.setObject(moDeformerSet); //need the fn to get the members if (!stat) { cout << "WARNING: Ignoring joint cluster because could not read deformer set object." << endl; continue; } stat = fnSet.getMembers(SetList, true); if (!stat) { cout << "WARNING: Ignoring joint cluster because could not get members from set list." << endl; continue; } for (iSet = 0; iSet < SetList.length(); iSet++) { stat = SetList.getDagPath(iSet, dagPath, moComponents); if (stat && dagPath == mdpMesh) break; } if (iSet == SetList.length()) { cout << "WARNING: Ignoring joint cluster because mesh DAG path was not found in set list." << endl; continue; } // get affected weights & points stat = fnCluster.getWeights(dagPath, moComponents, Weights); if (!stat) { cout << "WARNING: Ignoring joint cluster because could not read weights & components." << endl; continue; } cWeights = Weights.length(); if (0 == cWeights) continue; // BIND POSE moWorldBindMatrix = MObject::kNullObj; moLocalBindMatrix = MObject::kNullObj; mpToBindPose = fnBone.findPlug("bindPose", &stat); if (stat) // success { stat = mpToBindPose.getValue(moWorldBindMatrix); if (!stat) { cout << "WARNING: Ignoring bone. (Could not get 'bindPose' plug value.)" << endl; continue; } } else // failure { mpToBindPose = fnBone.findPlug("message", &stat); if (!stat) { cout << "WARNING: Ignoring bone. (Could not find 'message' plug.)" << endl; continue; } } // attempt to read local transform (preferable), and possibly world transform (if it wasn't read before) do { MPlug mpDagPose; MFnDependencyNode fnDagPose; MPlugArray mapConnections; MObject moAttribWM, moAttribXM; mpToBindPose.connectedTo(mapConnections, false, true, &stat); if (!stat) break; if (mapConnections.length() == 0) { stat = MS::kFailure; break; } mpDagPose = mapConnections[0]; // TODO: search through all connections instead of simply picking the first one. stat = fnDagPose.setObject(mpDagPose.node()); if (!stat) break; // world bind pose matrix (overwrites moWorldBindMatrix if read previously) while (moWorldBindMatrix.isNull()) { moAttribWM = fnDagPose.attribute("worldMatrix", &stat); if (!stat) break; MPlug mpWorldMatrix(mpDagPose.node(), moAttribWM); stat = mpWorldMatrix.selectAncestorLogicalIndex(mpDagPose.logicalIndex(), moAttribWM); if (!stat) break; stat = mpWorldMatrix.getValue(moWorldBindMatrix); if (!stat) break; } // local bind pose matrix while (moLocalBindMatrix.isNull()) { moAttribXM = fnDagPose.attribute("xformMatrix", &stat); if (!stat) break; MPlug mpXformMatrix(mpDagPose.node(), moAttribXM); stat = mpXformMatrix.selectAncestorLogicalIndex(mpDagPose.logicalIndex(), moAttribXM); if (!stat) break; stat = mpXformMatrix.getValue(moLocalBindMatrix); if (!stat) break; } break; } while (false); if (moWorldBindMatrix.isNull() && moLocalBindMatrix.isNull()) { cout << "WARNING: Ignoring bone. (Neither local nor world bind matrices could be obtained.)" << endl; continue; } // get bone name delete[] szBone; szBone = NULL; szBone = new char[1 + fnBone.partialPathName().length()]; if (NULL == szBone) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate bone name." << endl; goto e_Exit; } strcpy(szBone, fnBone.partialPathName().asChar()); for (pcBone = szBone; *pcBone != '\0'; pcBone++) { // replace '|' and ' ' characters by '_' in the maya partial pathname if (*pcBone == ' ' || *pcBone == '|') *pcBone = '_'; } for (iBoneIdx = 0; iBoneIdx < pShape->m_nBones; iBoneIdx++) { if (!strcmp(pShape->m_aBones[iBoneIdx].m_szName, szBone)) break; } // new bone if (iBoneIdx == cBonesMax) { // double array size SBone* aBones; bool** aabBPT; cBonesMax *= 2; aBones = NULL; aBones = new SBone[cBonesMax]; if (NULL == aBones) { cout << "LoadPolyMesh(): Could not allocate bones." << endl; goto e_Exit; } memcpy(aBones, pShape->m_aBones, pShape->m_nBones * sizeof(SBone)); aabBPT = NULL; aabBPT = new bool*[cBonesMax]; if (NULL == aabBPT) { cout << "LoadPolyMesh(): Could not allocate bones." << endl; goto e_Exit; } memset(aabBPT, 0, cBonesMax * sizeof(bool*)); // NULLify the table memcpy(aabBPT, aabBonePointTable, pShape->m_nBones * sizeof(bool*)); delete[] pShape->m_aBones; delete[] aabBonePointTable; pShape->m_aBones = aBones; aabBonePointTable = aabBPT; } if (iBoneIdx == pShape->m_nBones) { // initialize new bone aabBonePointTable[iBoneIdx] = new bool[pShape->m_nPoints]; if (NULL == aabBonePointTable) { cout << "LoadPolyMesh(): Could not allocate new column for bone point table." << endl; goto e_Exit; } memset(aabBonePointTable[iBoneIdx], 0, pShape->m_nPoints * sizeof(bool)); // falsify entries // save bone name pShape->m_aBones[iBoneIdx].m_szName = szBone; g_Arrays.Add(STRING, pShape->m_aBones[iBoneIdx].m_szName); szBone = NULL; // so that we won't later delete it maoBones.append(MObject::kNullObj); maoWorldBindMatrices.append(MObject::kNullObj); maoLocalBindMatrices.append(MObject::kNullObj); pShape->m_nBones++; } // save object & bind position maoBones[iBoneIdx] = fnBone.object(); maoWorldBindMatrices[iBoneIdx] = moWorldBindMatrix; maoLocalBindMatrices[iBoneIdx] = moLocalBindMatrix; // get the points & weights afWeights = NULL; afWeights = new float[pShape->m_aBones[iBoneIdx].m_nWeights + cWeights]; if (NULL == afWeights) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for bone weights." << endl; goto e_Exit; } memcpy (afWeights, pShape->m_aBones[iBoneIdx].m_afWeights, pShape->m_aBones[iBoneIdx].m_nWeights * sizeof(float)); delete[] pShape->m_aBones[iBoneIdx].m_afWeights; pShape->m_aBones[iBoneIdx].m_afWeights = afWeights; afWeights = NULL; aiPoints = NULL; aiPoints = new UINT[pShape->m_aBones[iBoneIdx].m_nWeights + cWeights]; if (NULL == aiPoints) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for bone influenced points." << endl; goto e_Exit; } memcpy (aiPoints, pShape->m_aBones[iBoneIdx].m_aiPoints, pShape->m_aBones[iBoneIdx].m_nWeights * sizeof(UINT)); delete[] pShape->m_aBones[iBoneIdx].m_aiPoints; pShape->m_aBones[iBoneIdx].m_aiPoints = aiPoints; aiPoints = NULL; MItGeometry itPoints(dagPath, moComponents); if ((UINT)itPoints.count() != cWeights) { hr = E_FAIL; cout << "WARNING: Ignoring bone because number of points differed from number of weights." << endl; goto e_Exit; } for (iWeight = 0; !itPoints.isDone(); itPoints.next(), iWeight++) { UINT iIndex = itPoints.index(); if (iIndex >= pShape->m_nPoints) { cout << "WARNING: Bone affects invalid index - ignoring..." << endl; continue; } if (aabBonePointTable[iBoneIdx][iIndex]) { cout << "WARNING: Ignoring the effect of this bone on this point because the point was previously affected by this bone before." << endl; continue; } pShape->m_aBones[iBoneIdx].m_aiPoints[pShape->m_aBones[iBoneIdx].m_nWeights] = iIndex; pShape->m_aBones[iBoneIdx].m_afWeights[pShape->m_aBones[iBoneIdx].m_nWeights] = Weights[iWeight]; pShape->m_aBones[iBoneIdx].m_nWeights++; pShape->m_aBones[iBoneIdx].m_nReps += pShape->m_aReps[iIndex].m_nReps; acBonesPerPoint[iIndex]++; aabBonePointTable[iBoneIdx][iIndex] = true; } if (!bRigidSkinning) { cout << "\t\t\tRigid skinning:"; bRigidSkinning = true; } cout << " " << fnBone.name().asChar(); // now add parent bone stat = fnDagNode.setObject(fnBone.object()); if (!stat) { hr = E_FAIL; cout << "LoadPolyMesh(): Could not get bone read DAG bone object." << endl; goto e_Exit; } while (fnDagNode.parentCount() > 0 && !fnDagNode.parent(0).hasFn(MFn::kTransform)) { stat = fnDagNode.setObject(fnDagNode.parent(0)); if (!stat) { hr = E_FAIL; cout << "LoadPolyMesh(): Could not get parent object." << endl; goto e_Exit; } } // get parent bone name if (fnDagNode.parentCount() == 0) continue; stat = fnDagNode.setObject(fnDagNode.parent(0)); if (!stat) { hr = E_FAIL; cout << "LoadPolyMesh(): Could not get parent object." << endl; goto e_Exit; } stat = fnDagNode.getPath(dagPath); if (!stat) { hr = E_FAIL; cout << "LoadPolyMesh(): Could not get parent's path." << endl; goto e_Exit; } delete[] szBone; szBone = NULL; szBone = new char[1 + dagPath.partialPathName().length()]; if (NULL == szBone) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate bone name." << endl; goto e_Exit; } strcpy(szBone, dagPath.partialPathName().asChar()); for (pcBone = szBone; *pcBone != '\0'; pcBone++) { // replace '|' and ' ' characters by '_' in the maya partial pathname if (*pcBone == ' ' || *pcBone == '|') *pcBone = '_'; } for (iBoneIdx = 0; iBoneIdx < pShape->m_nBones; iBoneIdx++) { if (!strcmp(pShape->m_aBones[iBoneIdx].m_szName, szBone)) break; } // new bone if (iBoneIdx == cBonesMax) { // double array size SBone* aBones; bool** aabBPT; cBonesMax *= 2; aBones = NULL; aBones = new SBone[cBonesMax]; if (NULL == aBones) { cout << "LoadPolyMesh(): Could not allocate bones." << endl; goto e_Exit; } memcpy(aBones, pShape->m_aBones, pShape->m_nBones * sizeof(SBone)); aabBPT = NULL; aabBPT = new bool*[cBonesMax]; if (NULL == aabBPT) { cout << "LoadPolyMesh(): Could not allocate bones." << endl; goto e_Exit; } memset(aabBPT, 0, cBonesMax * sizeof(bool*)); // NULLify the table memcpy(aabBPT, aabBonePointTable, pShape->m_nBones * sizeof(bool*)); delete[] pShape->m_aBones; delete[] aabBonePointTable; pShape->m_aBones = aBones; aabBonePointTable = aabBPT; } if (iBoneIdx == pShape->m_nBones) { // initialize new bone aabBonePointTable[iBoneIdx] = new bool[pShape->m_nPoints]; if (NULL == aabBonePointTable) { cout << "LoadPolyMesh(): Could not allocate new column for bone point table." << endl; goto e_Exit; } memset(aabBonePointTable[iBoneIdx], 0, pShape->m_nPoints * sizeof(bool)); // falsify entries // save bone name pShape->m_aBones[iBoneIdx].m_szName = szBone; g_Arrays.Add(STRING, pShape->m_aBones[iBoneIdx].m_szName); szBone = NULL; // so that we won't later delete it // offset matrix - it's a subtle point that we need to overwrite this each time (especially in the case of rigid skinning) stat = (mmWorldTransform).get(pShape->m_aBones[iBoneIdx].m_aafOffset); if (!stat) { hr = E_FAIL; cout << "LoadPolyMesh(): Could not get bind pose transform." << endl; goto e_Exit; } maoBones.append(MObject::kNullObj); maoWorldBindMatrices.append(MObject::kNullObj); maoLocalBindMatrices.append(MObject::kNullObj); pShape->m_nBones++; } // get the points & weights afWeights = NULL; afWeights = new float[pShape->m_aBones[iBoneIdx].m_nWeights + cWeights]; if (NULL == afWeights) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for bone weights." << endl; goto e_Exit; } memcpy (afWeights, pShape->m_aBones[iBoneIdx].m_afWeights, pShape->m_aBones[iBoneIdx].m_nWeights * sizeof(float)); delete[] pShape->m_aBones[iBoneIdx].m_afWeights; pShape->m_aBones[iBoneIdx].m_afWeights = afWeights; afWeights = NULL; aiPoints = NULL; aiPoints = new UINT[pShape->m_aBones[iBoneIdx].m_nWeights + cWeights]; if (NULL == aiPoints) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate memory for bone influenced points." << endl; goto e_Exit; } memcpy (aiPoints, pShape->m_aBones[iBoneIdx].m_aiPoints, pShape->m_aBones[iBoneIdx].m_nWeights * sizeof(UINT)); delete[] pShape->m_aBones[iBoneIdx].m_aiPoints; pShape->m_aBones[iBoneIdx].m_aiPoints = aiPoints; aiPoints = NULL; itPoints.reset(); for (iWeight = 0; !itPoints.isDone(); itPoints.next(), iWeight++) { UINT iIndex = itPoints.index(); if (iIndex >= pShape->m_nPoints) { cout << "WARNING: Bone affects invalid index - ignoring..." << endl; continue; } if (aabBonePointTable[iBoneIdx][iIndex]) { cout << "WARNING: Ignoring the effect of this bone on this point because the point was previously affected by this bone before." << endl; continue; } if (1.0f - Weights[iWeight] == 0.0f) continue; pShape->m_aBones[iBoneIdx].m_aiPoints[pShape->m_aBones[iBoneIdx].m_nWeights] = iIndex; pShape->m_aBones[iBoneIdx].m_afWeights[pShape->m_aBones[iBoneIdx].m_nWeights] = 1.0f - Weights[iWeight]; pShape->m_aBones[iBoneIdx].m_nWeights++; pShape->m_aBones[iBoneIdx].m_nReps += pShape->m_aReps[iIndex].m_nReps; acBonesPerPoint[iIndex]++; aabBonePointTable[iBoneIdx][iIndex] = true; } } e_ExitRigidSkinning: if (bRigidSkinning) cout << endl; // OTHER SKINNING stat = itClusters.reset(MFn::kWeightGeometryFilt); if (!stat) { cout << "WARNING: Could not search weight geometry filters." << endl; goto e_ExitSkinning; } for (bUnkownSkinning = false; !itClusters.isDone(); itClusters.next()) { if (itClusters.item().hasFn(MFn::kJointCluster)) continue; stat = fnCluster.setObject(itClusters.item()); if (!stat) { cout << "WARNING: Ignoring this weight geometry filter because couldn't read the object." << endl; continue; } cout << "\t\t\tUnknown skinning: (" << fnCluster.object().apiTypeStr() << ")." << endl; if (!bUnkownSkinning) bUnkownSkinning = true; } e_ExitSkinning: if ((UINT)maoBones.length() != pShape->m_nBones) // sanity check { cout << "LoadPolyMesh(): Aborting because there was a mismatch in the length of the bone- and the bone object- arrays." << endl; goto e_Exit; } amtmBonePositions = new MTransformationMatrix[pShape->m_nBones]; if (NULL == amtmBonePositions) { cout << "LoadPolyMesh(): Could not allocate bone transformation array." << endl; goto e_Exit; } // create bone remap array based on descending hierarchical order for (iBone = 0; iBone < pShape->m_nBones; iBone++) { if (maoBones[iBone].isNull() || !maoBones[iBone].hasFn(MFn::kJoint)) continue; stat = fnBone.setObject(maoBones[iBone]); if (!stat) continue; amtmBonePositions[iBone] = fnBone.transformation(); if (maoLocalBindMatrices[iBone].isNull()) continue; stat = fnMatrix.setObject(maoLocalBindMatrices[iBone]); if (!stat) continue; stat = fnBone.set(fnMatrix.transformation()); if (!stat) continue; } for (; !itBones.isDone(); itBones.next()) { MObject moBone = itBones.item(); for (iBone = 0; iBone < pShape->m_nBones; iBone++) { if (!maoLocalBindMatrices[iBone].isNull()) break; if (moBone == maoBones[iBone]) { maiBoneRemap.append(iBone); break; } } } for (iBoneRemap = 0; iBoneRemap < maiBoneRemap.length(); iBoneRemap++) { MDagPath mdpBone; iBone = maiBoneRemap[iBoneRemap]; if (maoBones[iBone].isNull() || !maoBones[iBone].hasFn(MFn::kJoint)) continue; stat = fnBone.setObject(maoBones[iBone]); if (!stat) continue; stat = fnBone.getPath(mdpBone); if (!stat) continue; if (maoWorldBindMatrices[iBone].isNull()) continue; stat = fnMatrix.setObject(maoWorldBindMatrices[iBone]); if (!stat) continue; stat = fnBone.set(MTransformationMatrix(fnMatrix.matrix() * mdpBone.exclusiveMatrixInverse())); if (!stat) continue; } for (iBone = 0; iBone < pShape->m_nBones; iBone++) { MMatrix mmBoneOffsetMatrix = mmWorldTransform; MDagPath mdpBone; do { MDagPath mdpBone; if (maoBones[iBone].isNull()) break; stat = MDagPath::getAPathTo(maoBones[iBone], mdpBone); if (!stat) { cout << "WARNING: Could not get DAG path to bone. Skinning may look incorrect." << endl; break; } mmBoneOffsetMatrix = mmBoneOffsetMatrix * mdpBone.inclusiveMatrix().inverse(); } while (false); mmBoneOffsetMatrix.get(pShape->m_aBones[iBone].m_aafOffset); } stat = fnMesh.syncObject(); if (!stat) { cout << "LoadPolyMesh(): Could not sync mesh object." << endl; goto e_ExitGeometry; } // POINTS stat = fnMesh.getPoints(mfpaPoints); if (!stat) { cout << "WARNING: Aborting because vertices could not be read." << endl; goto e_ExitGeometry; } if (mfpaPoints.length() != pShape->m_nPoints) { cout << "WARNING: Aborting because point array length(" << mfpaPoints.length() << ") did not match number of points(" << pShape->m_nPoints << ")." << endl; goto e_ExitGeometry; } for (iVert = 0; iVert < pShape->m_nPoints; iVert++) { pShape->m_aPoints[iVert].vec[0] = mfpaPoints[iVert][0]; pShape->m_aPoints[iVert].vec[1] = mfpaPoints[iVert][1]; pShape->m_aPoints[iVert].vec[2] = mfpaPoints[iVert][2]; } // NORMALS if (pShape->m_nNormals == 0) goto e_ExitNormals; stat = fnMesh.getNormals(mfvaNormals); if (!stat) { cout << "LoadPolyMesh(): Could not be read normals." << endl; goto e_ExitGeometry; } if (mfvaNormals.length() != pShape->m_nNormals) { cout << "LoadPolyMesh: Normal array length did not match number of normals." << endl; goto e_ExitGeometry; } for (iNorm = 0; iNorm < pShape->m_nNormals; iNorm++) { pShape->m_aNormals[iNorm].vec[0] = mfvaNormals[iNorm][0]; pShape->m_aNormals[iNorm].vec[1] = mfvaNormals[iNorm][1]; pShape->m_aNormals[iNorm].vec[2] = mfvaNormals[iNorm][2]; } e_ExitNormals: // UVs if (pShape->m_nUVs == 0) goto e_ExitUVs; stat = fnMesh.getUVs(mfaUs, mfaVs); if (!stat) { cout << "WARNING: Aborting because UVs could not be read." << endl; goto e_ExitGeometry; } if (mfaUs.length() != pShape->m_nUVs || mfaVs.length() != pShape->m_nUVs) // sanity check { cout << "WARNING: Aborting because UV array length did not match number of UVs." << endl; goto e_ExitGeometry; } for (iUV = 0; iUV < pShape->m_nUVs; iUV++) { pShape->m_aUVs[iUV].vec[0] = mfaUs[iUV]; pShape->m_aUVs[iUV].vec[1] = mfaVs[iUV]; } e_ExitUVs: e_ExitGeometry: for (iBone = 0; iBone < pShape->m_nBones; iBone++) { if (maoBones[iBone].isNull() || !maoBones[iBone].hasFn(MFn::kJoint)) continue; stat = fnBone.setObject(maoBones[iBone]); if (!stat) continue; stat = fnBone.set(amtmBonePositions[iBone]); if (!stat) continue; } if (FAILED(hr)) { cout << "LoadPolyMesh(): Could not load geometry." << endl; goto e_Exit; } // ensure that weights sum to 1 by adding, if necessary, an EXTRA BONE (corresponding to this mesh's transform) if (pShape->m_nBones > 0) { UINT nWeights = 0; UINT nReps = 0; afWeights = NULL; if (NULL == (afWeights = new float[pShape->m_nPoints])) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate extra bone's weight array." << endl; goto e_Exit; } memset(afWeights, 0, pShape->m_nPoints * sizeof(float)); // zero out array aiPoints = NULL; if (NULL == (aiPoints = new UINT[pShape->m_nPoints])) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate extra bone's point index array." << endl; goto e_Exit; } abBonePointArray = NULL; if (NULL == (abBonePointArray = new bool[pShape->m_nPoints])) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate extra bone's point flag array." << endl; goto e_Exit; } memset(abBonePointArray, 0, pShape->m_nPoints * sizeof(bool)); // falsify array for (iBone = 0; iBone < pShape->m_nBones; iBone++) { for (iIndex = 0; iIndex < pShape->m_aBones[iBone].m_nWeights; iIndex++) { afWeights[pShape->m_aBones[iBone].m_aiPoints[iIndex]] += pShape->m_aBones[iBone].m_afWeights[iIndex]; } } for (nWeights = 0, nReps = 0, iVert = 0; iVert < pShape->m_nPoints; iVert++) { if (afWeights[iVert] < 1.0f) { aiPoints[nWeights] = iVert; afWeights[nWeights] = 1.0f - afWeights[iVert]; nReps += pShape->m_aReps[iVert].m_nReps; nWeights++; acBonesPerPoint[iVert]++; abBonePointArray[iVert] = true; } } if (nWeights > 0) { // get bone name delete[] szBone; szBone = NULL; szBone = new char[1 + mdpTransform.partialPathName().length()]; if (NULL == szBone) { hr = E_OUTOFMEMORY; cout << "LoadPolyMesh(): Could not allocate bone name." << endl; goto e_Exit; } strcpy(szBone, mdpTransform.partialPathName().asChar()); for (pcBone = szBone; *pcBone != '\0'; pcBone++) { // replace '|' and ' ' characters by '_' in the maya partial pathname if (*pcBone == ' ' || *pcBone == '|') *pcBone = '_'; } iBoneIdx = pShape->m_nBones; // new bone if (iBoneIdx == cBonesMax) { // double array size SBone* aBones; bool** aabBPT; cBonesMax *= 2; aBones = NULL; aBones = new SBone[cBonesMax]; if (NULL == aBones) { cout << "LoadPolyMesh(): Could not allocate bones." << endl; goto e_Exit; } memcpy(aBones, pShape->m_aBones, pShape->m_nBones * sizeof(SBone)); aabBPT = NULL; aabBPT = new bool*[cBonesMax]; if (NULL == aabBPT) { cout << "LoadPolyMesh(): Could not allocate bones." << endl; goto e_Exit; } memset(aabBPT, 0, cBonesMax * sizeof(bool*)); // NULLify the table memcpy(aabBPT, aabBonePointTable, pShape->m_nBones * sizeof(bool*)); delete[] pShape->m_aBones; delete[] aabBonePointTable; pShape->m_aBones = aBones; aabBonePointTable = aabBPT; } if (iBoneIdx == pShape->m_nBones) { // initialize new bone // save bone name pShape->m_aBones[iBoneIdx].m_szName = szBone; g_Arrays.Add(STRING, pShape->m_aBones[iBoneIdx].m_szName); szBone = NULL; // so that we won't later delete it pShape->m_nBones++; } pShape->m_aBones[iBoneIdx].m_nWeights = nWeights; pShape->m_aBones[iBoneIdx].m_nReps = nReps; pShape->m_aBones[iBoneIdx].m_afWeights = afWeights; afWeights = NULL; pShape->m_aBones[iBoneIdx].m_aiPoints = aiPoints; aiPoints = NULL; aabBonePointTable[iBoneIdx] = abBonePointArray; abBonePointArray = NULL; } } // MAX BONES PER POINT pShape->m_nMaxBonesPerPoint = 0; for (iVert = 0; iVert < pShape->m_nPoints; iVert++) { if (pShape->m_nMaxBonesPerPoint < acBonesPerPoint[iVert]) pShape->m_nMaxBonesPerPoint = acBonesPerPoint[iVert]; } // MAX BONES PER FACE pShape->m_nMaxBonesPerFace = 0; for (iFace = 0; iFace < pShape->m_nFaces; iFace++) { UINT cBonesPerFace = 0; for (iBone = 0; iBone < pShape->m_nBones; iBone++) { for (iIndex = 0; iIndex < pShape->m_aFaces[iFace].m_nIndices; iIndex++) { if (aabBonePointTable[iBone][pShape->m_aReps[pShape->m_aFaces[iFace].m_aIndices[iIndex]].m_iFirst]) { cBonesPerFace++; break; } } } if (pShape->m_nMaxBonesPerFace < cBonesPerFace) pShape->m_nMaxBonesPerFace = cBonesPerFace; } pShape->m_kType = SMesh::POLY_MESH; // mesh type cout << "\t\t\t" << pShape->m_nReps << " rep(s), " << pShape->m_nPoints << " point(s), " << pShape->m_nNormals << " normal(s), " << pShape->m_nUVs << " UV(s), " << pShape->m_nGroups << " material(s), " << pShape->m_nFaces << " face(s), " << pShape->m_nBones << " bone(s), " << pShape->m_nMaxBonesPerPoint << " max bones/point, " << pShape->m_nMaxBonesPerFace << " max bones/face." << endl; e_Exit: if (NULL != aabBonePointTable) { for (iBone = 0; iBone < cBonesMax; iBone++) { delete[] aabBonePointTable[iBone]; } delete[] aabBonePointTable; } delete[] acBonesPerPoint; delete[] szBone; delete[] amtmBonePositions; delete[] afWeights; delete[] aiPoints; delete[] abBonePointArray; return hr; } bool IsPatchMesh ( MDagPath& mdpNurbs ) { MFnNurbsSurface fnNurbs; if (!mdpNurbs.hasFn(MFn::kNurbsSurface)) return false; // not a nurbs surface if (!fnNurbs.setObject(mdpNurbs)) return false; if (fnNurbs.degreeU() != 3 || fnNurbs.degreeV() != 3) return false; // not a bicubic surface int kFormInU = fnNurbs.formInU(); int kFormInV = fnNurbs.formInV(); if (kFormInU == MFnNurbsSurface::kInvalid || kFormInV == MFnNurbsSurface::kInvalid) return false; // surface has invalid form if (kFormInU == MFnNurbsSurface::kPeriodic || kFormInV == MFnNurbsSurface::kPeriodic) return false; // can't handle periodic surfaces int cCVsInU = fnNurbs.numCVsInU(); int cCVsInV = fnNurbs.numCVsInV(); if ((cCVsInU - 1) % 3 != 0 || (cCVsInV - 1) % 3 != 0) return false; // invalid control point count (we only deal with quad patches) int cSpansInU = (cCVsInU - 1) / 3; int cSpansInV = (cCVsInV - 1) / 3; if (cSpansInU <= 0 || cSpansInV <= 0) return false; // invalid span count return true; // all tests passed } HRESULT LoadPatchMesh ( MDagPath& mdpTransform, MDagPath& mdpNurbs, SMesh* pShape ) { HRESULT hr = S_OK; MStatus stat = MS::kSuccess; MFnTransform fnTransform; MFnNurbsSurface fnNurbs; MDagPath mdpMesh; MObject moMesh = MObject::kNullObj; if (!mdpTransform.hasFn(MFn::kTransform)) { hr = E_FAIL; cout << "LoadPatchMesh(): Input path is not a transform as expected. Aborting..." << endl; goto e_Exit; } stat = fnTransform.setObject(mdpTransform); if (!stat) { hr = E_FAIL; cout << "LoadPatchMesh(): Could not access transform object through function set. Aborting..." << endl; goto e_Exit; } if (!mdpNurbs.hasFn(MFn::kNurbsSurface)) { hr = E_FAIL; cout << "LoadPatchMesh(): Input path is not a NURBS surface as expected. Aborting..." << endl; goto e_Exit; } stat = fnNurbs.setObject(mdpNurbs); if (!stat) { hr = E_FAIL; cout << "LoadPatchMesh(): Could not access NURBS surface object through function set. Aborting..." << endl; goto e_Exit; } if (g_bExportPatches) goto e_PatchExport; // TESSELATE moMesh = fnNurbs.tesselate(MTesselationParams::fsDefaultTesselationParams, mdpTransform.node(), &stat); if (!stat) { hr = E_FAIL; cout << "LoadPatchMesh(): Could not tesselate NURBS surface. Aborting..." << endl; goto e_ExitTesselate; } if (!moMesh.hasFn(MFn::kMesh)) { hr = E_FAIL; cout << "LoadPatchMesh(): Tesselation did not produce a mesh. Aborting..." << endl; goto e_ExitTesselate; } stat = MDagPath::getAPathTo(moMesh, mdpMesh); if (!stat) { hr = E_FAIL; cout << "LoadPatchMesh(): Could not a DAG path to the tesselated mesh. Aborting..." << endl; goto e_ExitTesselate; } hr = LoadPolyMesh(mdpTransform, mdpMesh, pShape); if (FAILED(hr)) { cout << "LoadPatchMesh(): Could not load tesselated mesh. Aborting..." << endl; goto e_ExitTesselate; } e_ExitTesselate: if (!moMesh.isNull() && !fnTransform.object().isNull()) { stat = fnTransform.removeChild(moMesh); if (!stat) cout << "WARNING: Could not remove tesselated mesh object." << endl; } goto e_Exit; e_PatchExport: /* // set up references // ensure that shape is a nurbs surface MObject objNurb; DT_ATTEMPT(DtExt_ShapeGetShapeNode(iShape, objNurb)); ASSERT(objNurb.hasFn(MFn::kNurbsSurface), "Not a nurb surface"); MFnNurbsSurface fnNurb(objNurb); // ensure that surface is bicubic ASSERT(fnNurb.degreeU() == 3 && fnNurb.degreeV() == 3, "Not a bicubic surface"); // ensure correct form in U and V int kFormInU = fnNurb.formInU(); int kFormInV = fnNurb.formInV(); ASSERT(kFormInU == MFnNurbsSurface::kOpen || kFormInU == MFnNurbsSurface::kClosed, "Invalid form in U"); ASSERT(kFormInV == MFnNurbsSurface::kOpen || kFormInV == MFnNurbsSurface::kClosed, "Invalid form in V"); // ensure that surface is a quad mesh int cCVsInU = fnNurb.numCVsInU(); int cCVsInV = fnNurb.numCVsInV(); ASSERT((cCVsInU - 1) % 3 == 0 && (cCVsInV - 1) % 3 == 0, "Invalid CV count"); // ensure that there is at least one patch int cSpansInU = (cCVsInU - 1) / 3; int cSpansInV = (cCVsInV - 1) / 3; ASSERT(cSpansInU > 0 && cSpansInV > 0, "Invalid span count"); // control vertices MPointArray rgCVs; fnNurb.getCVs(rgCVs); cVertices = (int)rgCVs.length(); rgVertices = new DtVec3f[cVertices]; ASSERT(rgVertices, "Can't allocate memory for vertex array"); for (UINT iVertex = 0; iVertex < cVertices; iVertex++) { rgVertices[iVertex].vec[0] = (float)rgCVs[iVertex][0]; rgVertices[iVertex].vec[1] = (float)rgCVs[iVertex][1]; rgVertices[iVertex].vec[2] = (float)rgCVs[iVertex][2]; } // texture coordinates cTexCoords = (int)rgCVs.length(); rgTexCoords = new DtVec2f[cTexCoords]; ASSERT(rgTexCoords, "Can't allocate memory for texture coordinate array"); for (int iCVInU = 0, iTexCoord = 0; iCVInU < cCVsInU; iCVInU++) { for (int iCVInV = 0; iCVInV < cCVsInV; iCVInV++, iTexCoord++) { rgTexCoords[iTexCoord].vec[0] = ((float)iCVInU) / ((float)(cCVsInU - 1)); rgTexCoords[iTexCoord].vec[1] = ((float)iCVInV) / ((float)(cCVsInV - 1)); } } // face info cFaces = cSpansInU * cSpansInV; rgFaces = new SFace[cFaces]; ASSERT(rgFaces, "Can't allocate memory for patch array"); cFaceIndices = 0; for (int iSpanInU = 0, iPatch = 0; iSpanInU < cSpansInU; iSpanInU++) { int iCVIndexInU = iSpanInU * 3; for (int iSpanInV = 0; iSpanInV < cSpansInV; iSpanInV++, iPatch++) { int iCVIndexInV = iSpanInV * 3; rgFaces[iPatch].m_nIndices = 16; rgFaces[iPatch].m_aIndices = new UINT[rgFaces[iPatch].m_nIndices]; ASSERT(rgFaces[iPatch].m_aIndices, "Could not allocate memory for patch indices"); rgFaces[iPatch].m_aIndices[0] = cCVsInV * (iCVIndexInU + 0) + (iCVIndexInV + 0); rgFaces[iPatch].m_aIndices[1] = cCVsInV * (iCVIndexInU + 1) + (iCVIndexInV + 0); rgFaces[iPatch].m_aIndices[2] = cCVsInV * (iCVIndexInU + 2) + (iCVIndexInV + 0); rgFaces[iPatch].m_aIndices[3] = cCVsInV * (iCVIndexInU + 3) + (iCVIndexInV + 0); rgFaces[iPatch].m_aIndices[4] = cCVsInV * (iCVIndexInU + 3) + (iCVIndexInV + 1); rgFaces[iPatch].m_aIndices[5] = cCVsInV * (iCVIndexInU + 3) + (iCVIndexInV + 2); rgFaces[iPatch].m_aIndices[6] = cCVsInV * (iCVIndexInU + 3) + (iCVIndexInV + 3); rgFaces[iPatch].m_aIndices[7] = cCVsInV * (iCVIndexInU + 2) + (iCVIndexInV + 3); rgFaces[iPatch].m_aIndices[8] = cCVsInV * (iCVIndexInU + 1) + (iCVIndexInV + 3); rgFaces[iPatch].m_aIndices[9] = cCVsInV * (iCVIndexInU + 0) + (iCVIndexInV + 3); rgFaces[iPatch].m_aIndices[10] = cCVsInV * (iCVIndexInU + 0) + (iCVIndexInV + 2); rgFaces[iPatch].m_aIndices[11] = cCVsInV * (iCVIndexInU + 0) + (iCVIndexInV + 1); rgFaces[iPatch].m_aIndices[12] = cCVsInV * (iCVIndexInU + 1) + (iCVIndexInV + 1); rgFaces[iPatch].m_aIndices[13] = cCVsInV * (iCVIndexInU + 2) + (iCVIndexInV + 1); rgFaces[iPatch].m_aIndices[14] = cCVsInV * (iCVIndexInU + 2) + (iCVIndexInV + 2); rgFaces[iPatch].m_aIndices[15] = cCVsInV * (iCVIndexInU + 1) + (iCVIndexInV + 2); rgFaces[iPatch].m_iGroup = 0; // WARNING: assumes only 1 material per surface cFaceIndices += rgFaces[iPatch].m_nIndices; } } // material info cGroups = DtGroupGetCount(iShape); rgGroups = new SGroup[cGroups]; ASSERT(rgGroups, "Can't allocate memory for material group array"); ASSERT(cGroups == 1, "Assumption was made that NURBS surfaces have only 1 material"); for (UINT iGroup = 0; iGroup < cGroups; iGroup++) { // material name DT_ATTEMPT(DtMtlGetName(iShape, iGroup, &rgGroups[iGroup].m_szMaterial)); // texture file name DT_ATTEMPT(MyDtTextureGetFileName(rgGroups[iGroup].m_szMaterial, &rgGroups[iGroup].m_szTextureFile)); // diffuse color if (!rgGroups[iGroup].m_szTextureFile) { DT_ATTEMPT(DtMtlGetDiffuseClr(rgGroups[iGroup].m_szMaterial, 0, &rgGroups[iGroup].m_fDiffuseRed, &rgGroups[iGroup].m_fDiffuseGreen, &rgGroups[iGroup].m_fDiffuseBlue)); } else // material has a texture { // load the diffuse factor into the diffuse components int iMaterial; DT_ATTEMPT(DtMtlGetID(iShape, iGroup, &iMaterial)); MObject mShader; DT_ATTEMPT(DtExt_MtlGetShader(iMaterial, mShader)); MFnLambertShader fnShader; fnShader.setObject(mShader); float fDiffuseFactor = fnShader.diffuseCoeff(); rgGroups[iGroup].m_fDiffuseRed = fDiffuseFactor; rgGroups[iGroup].m_fDiffuseGreen = fDiffuseFactor; rgGroups[iGroup].m_fDiffuseBlue = fDiffuseFactor; } // specular color DT_ATTEMPT(DtMtlGetSpecularClr(rgGroups[iGroup].m_szMaterial, 0, &rgGroups[iGroup].m_fSpecularRed, &rgGroups[iGroup].m_fSpecularGreen, &rgGroups[iGroup].m_fSpecularBlue)); // emissive color DT_ATTEMPT(DtMtlGetEmissiveClr(rgGroups[iGroup].m_szMaterial, 0, &rgGroups[iGroup].m_fEmissiveRed, &rgGroups[iGroup].m_fEmissiveGreen, &rgGroups[iGroup].m_fEmissiveBlue)); // power / shininess DT_ATTEMPT(DtMtlGetShininess(rgGroups[iGroup].m_szMaterial, 0, &rgGroups[iGroup].m_fShininess)); // transparency / alpha DT_ATTEMPT(DtMtlGetTransparency(rgGroups[iGroup].m_szMaterial, 0, &rgGroups[iGroup].m_fTransparency)); } // vertex duplication info (very simple for patch meshes) cReps = (int)rgCVs.length(); rgReps = new SRep[cReps]; ASSERT(rgReps, "Can't allocate memory for rep array"); for (UINT iRep = 0; iRep < cReps; iRep++) { rgReps[iRep].m_iNorm = -1; // patches don't export normal info rgReps[iRep].m_iUV = iRep; rgReps[iRep].m_iFirst = iRep; rgReps[iRep].m_iNext = iRep; rgReps[iRep].m_nReps = 1; } // skinning info MObject objShape; MObject objTransform; MObject objInput; DT_ATTEMPT(DtExt_ShapeGetShapeNode(iShape, objShape)); DT_ATTEMPT(DtExt_ShapeGetTransform(iShape, objTransform)); // load the mesh's world transform (needed if skinning info is found) MDagPath pathTransform; MFnDagNode(objTransform).getPath(pathTransform); MMatrix matMeshWorldTransform = pathTransform.inclusiveMatrix(); cBones = 0; rgBones = NULL; cMaxBonesPerVertex = 0; cMaxBonesPerFace = 0; MObjectArray rgobjBones; // smooth skinning bool* rgbNonZeroFlagTable = NULL; // table of influences vs. vertices UINT* rgcNonZeros = NULL; // array of influence counts bool bFoundSmoothSkin = false; if (objShape.hasFn(MFn::kNurbsSurface)) // if this shape is a mesh { // loop through skin clusters for (MItDependencyNodes itSkin(MFn::kSkinClusterFilter); !itSkin.isDone(); itSkin.next()) { MFnSkinCluster fnSkin(itSkin.item()); // load input and output geometries MObjectArray rgInputs; MObjectArray rgOutputs; fnSkin.getInputGeometry(rgInputs); fnSkin.getOutputGeometry(rgOutputs); assert(rgInputs.length() == rgOutputs.length()); // ensure that input geometry count // equals output geometry count int cInputs, cOutputs; cInputs = cOutputs = (int)rgOutputs.length(); // loop through the output geometries for (int iOutput = 0, iInput = 0; iOutput < cOutputs; iOutput++, iInput++) { assert(iOutput == iInput); // sanity check if (rgOutputs[iOutput] == objShape) // if our shape is one of the output geometries { MDagPathArray rgdagpathInfluences; cBones = (int)fnSkin.influenceObjects(rgdagpathInfluences, &mStat); rgBones = new SBone[cBones]; ASSERT(rgBones, "Could not allocate memory for bone array"); // initialize bones for (UINT iBone = 0; iBone < cBones; iBone++) { // WARNING: not checking for new failure rgBones[iBone].m_szName = new char[256]; rgBones[iBone].m_nReps = 0; rgBones[iBone].m_nWeights = 0; rgBones[iBone].m_aiPoints = new int[cVertices]; rgBones[iBone].m_afWeights = new float[cVertices]; g_Strings.add(rgBones[iBone].m_szName); // housekeeping // bone name strcpy(rgBones[iBone].m_szName, rgdagpathInfluences[iBone].partialPathName().asChar()); // matrix offset MFnIkJoint fnBone(rgdagpathInfluences[iBone]); MObject objBindPose; fnBone.findPlug("bindPose").getValue(objBindPose); MFnMatrixData fnBindPose(objBindPose); (matMeshWorldTransform * fnBindPose.matrix().inverse()).get(rgBones[iBone].m_matOffset); rgobjBones.append(rgdagpathInfluences[iBone].node()); } rgcNonZeros = new UINT[cVertices]; ASSERT(rgcNonZeros, "Could not allocate memory for non zero count array"); rgbNonZeroFlagTable = new bool[cVertices * cBones]; ASSERT(rgbNonZeroFlagTable, "Could not allocate memory for non zero table"); // bone info; calculate max number of bones per vertex cMaxBonesPerVertex = 0; int iVertex = 0; MFnNurbsSurface fnOutput(rgOutputs[iOutput]); MDagPath dagpathOutputShape; fnOutput.getPath(dagpathOutputShape); // loop through the vertices for (MItGeometry itGeom(rgOutputs[iOutput]); !itGeom.isDone(); itGeom.next()) { MFloatArray rgfWeights; unsigned cInfs; fnSkin.getWeights(dagpathOutputShape, itGeom.component(), rgfWeights, cInfs); int a = rgdagpathInfluences.length(); int b = rgfWeights.length(); int c = itGeom.count(); assert(rgdagpathInfluences.length() == rgfWeights.length()); assert(rgfWeights.length() == cInfs); rgcNonZeros[iVertex] = 0; float fWeightSum = 0.0f; for (UINT iBone = 0; iBone < cBones; iBone++) fWeightSum += rgfWeights[iBone]; assert(fWeightSum > 0.00001f); for (iBone = 0; iBone < cBones; iBone++) { rgbNonZeroFlagTable[iBone * cVertices + iVertex] = false; rgfWeights[iBone] = rgfWeights[iBone] / fWeightSum; // normalize the weight if (rgfWeights[iBone] != 0.0f) { rgcNonZeros[iVertex]++; rgBones[iBone].m_nReps += rgReps[iVertex].m_nReps; rgBones[iBone].m_aiPoints[rgBones[iBone].m_nWeights] = iVertex; rgBones[iBone].m_afWeights[rgBones[iBone].m_nWeights] = rgfWeights[iBone]; rgbNonZeroFlagTable[iBone * cVertices + iVertex] = true; rgBones[iBone].m_nWeights++; } } if (rgcNonZeros[iVertex] > cMaxBonesPerVertex) cMaxBonesPerVertex = rgcNonZeros[iVertex]; iVertex++; } // calculate max number of bones per vertex cMaxBonesPerFace = 0; for (UINT iFace = 0; iFace < cFaces; iFace++) { UINT cBonesPerFace = 0; for (UINT iBone = 0; iBone < cBones; iBone++) { for (UINT iIndex = 0; iIndex < rgFaces[iFace].m_nIndices; iIndex++) { if (rgbNonZeroFlagTable[iBone * cVertices + rgReps[rgFaces[iFace].m_aIndices[iIndex]].m_iFirst]) { cBonesPerFace++; break; } } } if (cBonesPerFace > cMaxBonesPerFace) cMaxBonesPerFace = cBonesPerFace; } objInput = rgInputs[iInput]; bFoundSmoothSkin = true; break; } } if (bFoundSmoothSkin) break; } } delete[] rgcNonZeros; delete[] rgbNonZeroFlagTable; // rigid skinning rgbNonZeroFlagTable = NULL; bool bFoundRigidSkin = false; if (!bFoundSmoothSkin) { cBones = 1; // zero'th bone is the extra "fake" bone UINT cBonesMax = 64; rgBones = new SBone[cBonesMax]; ASSERT(rgBones, "Could not allocate memory for bone array"); rgbNonZeroFlagTable = new bool[cBonesMax * cVertices]; ASSERT(rgbNonZeroFlagTable, "Could not allocate memory for non-zero flag table"); // fill non zero table with 0's memset(rgbNonZeroFlagTable, 0, cBonesMax * cVertices * sizeof(bool)); // initialize "fake" iBone // WARNING: not checking for new failure rgBones[0].m_szName = new char[256]; rgBones[0].m_nReps = 0; rgBones[0].m_nWeights = 0; rgBones[0].m_afWeights = new float[cVertices]; rgBones[0].m_aiPoints = new int[cVertices]; g_Strings.add(rgBones[0].m_szName); // housekeeping strcpy(rgBones[0].m_szName, SCENE_ROOT); // bone name matMeshWorldTransform.get(rgBones[0].m_matOffset); // "fake" bone has identity matrix // loop through joint clusters for (MItDependencyNodes itCluster(MFn::kJointCluster); !itCluster.isDone(); itCluster.next()) { MFnWeightGeometryFilter fnCluster(itCluster.item()); // load input and output geometries MObjectArray rgInputs; MObjectArray rgOutputs; fnCluster.getInputGeometry(rgInputs); fnCluster.getOutputGeometry(rgOutputs); assert(rgInputs.length() == rgOutputs.length()); // ensure input geometry count equals // output geometry count int cInputs, cOutputs; cInputs = cOutputs = (int)rgOutputs.length(); // loop through the output geometries for (int iOutput = 0, iInput = 0; iOutput < cOutputs; iOutput++, iInput++) { assert(iOutput == iInput); if (rgOutputs[iOutput] == objShape) // our shape is one of the output geometries { bFoundRigidSkin = true; assert(rgInputs[iInput] == fnCluster.inputShapeAtIndex(iInput)); // sanity check objInput = rgInputs[iInput]; // get bone MPlug plgMatrix = fnCluster.findPlug("matrix", &mStat); MPlugArray rgplgMatrixConnections; plgMatrix.connectedTo(rgplgMatrixConnections, true, false); // get source plugs assert(rgplgMatrixConnections.length() == 1); MObject objBone = rgplgMatrixConnections[0].node(); assert(objBone.hasFn(MFn::kJoint)); MFnIkJoint fnBone(objBone); char szBone[64]; strcpy(szBone, fnBone.name().asChar()); // find bone's index in current bone list for (UINT iBone = 1; iBone < cBones; iBone++) { if (!strcmp(rgBones[iBone].m_szName, szBone)) break; } if (iBone == cBones) // bone was not found in current bone list { // add bone if (cBones >= cBonesMax) { // double array size cBonesMax += cBonesMax; SBone* rgNewBones = new SBone[cBonesMax]; ASSERT(rgNewBones, "Could not allocate memory for new bone array"); memcpy(rgNewBones, rgBones, cBones * sizeof(SBone)); delete[] rgBones; rgBones = rgNewBones; bool* rgbNewNonZeroFlagTable = new bool[cBonesMax * cVertices]; ASSERT(rgbNewNonZeroFlagTable, "Could not allocate memory for new non-zero flag table"); memset(rgbNewNonZeroFlagTable, 0, cBonesMax * cVertices * sizeof(bool)); memcpy(rgbNewNonZeroFlagTable, rgbNonZeroFlagTable, cBones * cVertices * sizeof(bool)); delete[] rgbNonZeroFlagTable; rgbNonZeroFlagTable = rgbNewNonZeroFlagTable; } // initialize iBone // WARNING: not checking for new failure rgBones[iBone].m_szName = new char[256]; rgBones[iBone].m_nReps = 0; rgBones[iBone].m_nWeights = 0; rgBones[iBone].m_aiPoints = new int[cVertices]; rgBones[iBone].m_afWeights = new float[cVertices]; g_Strings.add(rgBones[iBone].m_szName); // housekeeping strcpy(rgBones[iBone].m_szName, szBone); // bone name // matrix info MObject objBindPose; fnBone.findPlug("bindPose").getValue(objBindPose); MFnMatrixData fnBindPose(objBindPose); (matMeshWorldTransform * fnBindPose.matrix().inverse()).get(rgBones[iBone].m_matOffset); rgobjBones.append(objBone); cBones++; } char szParent[64]; bool bFoundParent = false; MObject objParent; for (UINT iParent = 0; iParent < fnBone.parentCount(); iParent++) { objParent = fnBone.parent(iParent); MFnDagNode fnParent(objParent); strcpy(szParent, fnParent.name().asChar()); // parent's name for (int iShape_ = 0; iShape_ < DtShapeGetCount(); iShape_++) { char* szShape; DT_ATTEMPT(DtShapeGetName(iShape_, &szShape)); if (!strcmp(szParent, szShape)) { bFoundParent = true; break; } } if (bFoundParent) break; } iParent = 0; if (bFoundParent) // parent shape found { // find parent bone's index in current bone list for (iParent = 1; iParent < cBones; iParent++) { if (!strcmp(rgBones[iParent].m_szName, szParent)) { break; } } if (iParent == cBones) // parent bone was not found in current bone list { // add parent bone if (cBones >= cBonesMax) { // double array size cBonesMax += cBonesMax; SBone* rgNewBones = new SBone[cBonesMax]; ASSERT(rgNewBones, "Could not allocate memory for new bone array"); memcpy(rgNewBones, rgBones, cBones * sizeof(SBone)); delete[] rgBones; rgBones = rgNewBones; bool* rgbNewNonZeroFlagTable = new bool[cBonesMax * cVertices]; ASSERT(rgbNewNonZeroFlagTable, "Could not allocate memory for new non-zero flag table"); memset(rgbNewNonZeroFlagTable, 0, cBonesMax * cVertices * sizeof(bool)); memcpy(rgbNewNonZeroFlagTable, rgbNonZeroFlagTable, cBones * cVertices * sizeof(bool)); delete[] rgbNonZeroFlagTable; rgbNonZeroFlagTable = rgbNewNonZeroFlagTable; } // initialize iBone // WARNING: not checking for new failure rgBones[iParent].m_szName = new char[256]; rgBones[iParent].m_nReps = 0; rgBones[iParent].m_nWeights = 0; rgBones[iParent].m_aiPoints = new int[cVertices]; rgBones[iParent].m_afWeights = new float[cVertices]; g_Strings.add(rgBones[iParent].m_szName); // housekeeping strcpy(rgBones[iParent].m_szName, szParent); // bone name // matrix info MObject objBindPose; assert(objParent.hasFn(MFn::kJoint)); MFnIkJoint(objParent).findPlug("bindPose").getValue(objBindPose); MFnMatrixData fnBindPose(objBindPose); (matMeshWorldTransform * fnBindPose.matrix().inverse()).get(rgBones[iParent].m_matOffset); rgobjBones.append(objParent); cBones++; } } // load weights MPlug plgMessage = fnCluster.findPlug("message"); MPlugArray rgplgMessageConnections; plgMessage.connectedTo(rgplgMessageConnections, false, true); // get destination plugs assert(rgplgMessageConnections.length() == 1); assert(rgplgMessageConnections[0].node().hasFn(MFn::kSet)); MFnSet fnSet(rgplgMessageConnections[0].node()); MSelectionList list; fnSet.getMembers(list, false); assert(list.length() == 1); MDagPath path; MObject objComponents; list.getDagPath(0, path, objComponents); MFloatArray rgWeights; fnCluster.getWeights(path, objComponents, rgWeights); assert(objComponents.hasFn(MFn::kDoubleIndexedComponent)); MFnDoubleIndexedComponent fnComponent(objComponents); assert(fnComponent.elementCount() == (int)rgWeights.length()); // loop through the weights for (int iWeight = 0; iWeight < (int)rgWeights.length(); iWeight++) { assert(rgWeights[iWeight] <= 1.0f); int iU, iV; fnComponent.getElement(iWeight, iU, iV); // WARNING: check calculation of iVertex int iVertex = iU * cCVsInV + iV; rgBones[iBone].m_afWeights[rgBones[iBone].m_nWeights] = rgWeights[iWeight]; rgBones[iBone].m_aiPoints[rgBones[iBone].m_nWeights] = iVertex; rgBones[iBone].m_nReps += rgReps[iVertex].m_nReps; rgBones[iBone].m_nWeights++; rgbNonZeroFlagTable[iBone * cVertices + iVertex] = true; if (rgWeights[iWeight] != 1.0f) { rgBones[iParent].m_afWeights[rgBones[iParent].m_nWeights] = 1.0f - rgWeights[iWeight]; rgBones[iParent].m_aiPoints[rgBones[iParent].m_nWeights] = iVertex; rgBones[iParent].m_nReps += rgReps[iVertex].m_nReps; rgBones[iParent].m_nWeights++; // IMPORTANT: Don't change line position rgbNonZeroFlagTable[iParent * cVertices + iVertex] = true; } } break; } // if found our mesh } // loop thru geom's } // loop thru joint clusters if (cBones == 1) // no rigid skinning found { delete[] rgBones[0].m_afWeights; delete[] rgBones[0].m_aiPoints; cBones = 0; } else { // at most 2 bones per vertex in rigid skinning (i.e. bone + parent) cMaxBonesPerVertex = 2; // calculate max number of bones per vertex cMaxBonesPerFace = 0; for (UINT iFace = 0; iFace < cFaces; iFace++) { UINT cBonesPerFace = 0; for (UINT iBone = 0; iBone < cBones; iBone++) { for (UINT iIndex = 0; iIndex < rgFaces[iFace].m_nIndices; iIndex++) { if (rgbNonZeroFlagTable[iBone * cVertices + rgReps[rgFaces[iFace].m_aIndices[iIndex]].m_iFirst]) { cBonesPerFace++; break; } } } if (cBonesPerFace > cMaxBonesPerFace) cMaxBonesPerFace = cBonesPerFace; } } } delete[] rgbNonZeroFlagTable; // reload control vertices if skinning info was found if (bFoundSmoothSkin || bFoundRigidSkin) { delete[] rgVertices; MyDtShapeGetControlPoints(objInput, objShape, &cVertices, &rgVertices); } // mesh type pShape->m_kType = SMesh::PATCH_MESH; */ e_Exit: return hr; } HRESULT AddSkin ( SMesh* pShape, LPDIRECTXFILEDATA pShapeDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; UINT iBone; LPDIRECTXFILEDATA pSkinDataObject = NULL; PBYTE pbSkinData = NULL; UINT cbSkinSize = sizeof(WORD) // nMaxSkinWeightsPerVertex + sizeof(WORD) // nMaxSkinWeightsPerFace + sizeof(WORD); // nBones PBYTE pbSkinCurr = pbSkinData = new BYTE[cbSkinSize]; if (pbSkinData == NULL) { hr = E_OUTOFMEMORY; cout << "AddSkin(): Could not allocate memory for pbSkinData." << endl; goto e_Exit1; } // nMaxSkinWeightsPerVertex WRITE_WORD(pbSkinCurr, ((WORD)pShape->m_nMaxBonesPerPoint)); // nMaxSkinWeightsPerFace WRITE_WORD(pbSkinCurr, ((WORD)pShape->m_nMaxBonesPerFace)); // nBones WRITE_WORD(pbSkinCurr, ((WORD)pShape->m_nBones)); hr = pxofSave->CreateDataObject(DXFILEOBJ_XSkinMeshHeader, NULL, NULL, cbSkinSize, pbSkinData, &pSkinDataObject); if (FAILED(hr)) { cout << "AddSkin(): Could not create pSkinDataObject." << endl; goto e_Exit1; } hr = pShapeDataObject->AddDataObject(pSkinDataObject); if (FAILED(hr)) { cout << "AddSkin(): Could not add pSkinDataObject to pShapeDataObject." << endl; goto e_Exit1; } e_Exit1: delete[] pbSkinData; if (pSkinDataObject) pSkinDataObject->Release(); if (FAILED(hr)) return hr; // SkinWeights for (iBone = 0; iBone < pShape->m_nBones; iBone++) { UINT iRow, iCol, iVertex; LPDIRECTXFILEDATA pBoneDataObject = NULL; PBYTE pbBoneData = NULL; UINT cbBoneSize = sizeof(char*) // transformNodeName + sizeof(DWORD) // nWeights + sizeof(DWORD) * pShape->m_aBones[iBone].m_nReps // vertexIndices[nWeights] + sizeof(float) * pShape->m_aBones[iBone].m_nReps // weights[nWeights] + sizeof(float) * 16; // matrixOffset PBYTE pbBoneCurr = pbBoneData = new BYTE[cbBoneSize]; if (pbBoneData == NULL) { hr = E_OUTOFMEMORY; cout << "AddSkin(): Could not allocate memory for pbBoneData." << endl; goto e_Exit2; } // transformNodeName WRITE_PCHAR(pbBoneCurr, ((char*)pShape->m_aBones[iBone].m_szName)); // nWeights WRITE_DWORD(pbBoneCurr, ((DWORD)pShape->m_aBones[iBone].m_nReps)); // vertexIndices[nWeights] for (iVertex = 0; iVertex < pShape->m_aBones[iBone].m_nWeights; iVertex++) { UINT iRep = pShape->m_aBones[iBone].m_aiPoints[iVertex]; do { WRITE_DWORD(pbBoneCurr, ((DWORD)iRep)); iRep = pShape->m_aReps[iRep].m_iNext; } while (iRep != pShape->m_aReps[iRep].m_iFirst); } // weights[nWeights] for (iVertex = 0; iVertex < pShape->m_aBones[iBone].m_nWeights; iVertex++) { for (UINT iRep = 0; iRep < pShape->m_aReps[pShape->m_aBones[iBone].m_aiPoints[iVertex]].m_nReps; iRep++) { WRITE_FLOAT(pbBoneCurr, pShape->m_aBones[iBone].m_afWeights[iVertex]); } } // matrixOffset for (iRow = 0; iRow < 4; iRow++) { for (iCol = 0; iCol < 4; iCol++) { WRITE_FLOAT(pbBoneCurr, pShape->m_aBones[iBone].m_aafOffset[iRow][iCol]); } } hr = pxofSave->CreateDataObject(DXFILEOBJ_SkinWeights, NULL, NULL, cbBoneSize, pbBoneData, &pBoneDataObject); if (FAILED(hr)) { cout << "AddSkin(): Could not create pBoneDataObject." << endl; goto e_Exit2; } hr = pShapeDataObject->AddDataObject(pBoneDataObject); if (FAILED(hr)) { cout << "AddSkin(): Could not add pBoneDataObject to pShapeDataObject." << endl; goto e_Exit2; } e_Exit2: delete[] pbBoneData; if (pBoneDataObject) pBoneDataObject->Release(); if (FAILED(hr)) return hr; } return hr; } HRESULT AddNormals ( SMesh* pShape, LPDIRECTXFILEDATA pShapeDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; LPDIRECTXFILEDATA pNormalsDataObject = NULL; PBYTE pbNormalsData = NULL; int cbNormalsSize = sizeof(DWORD) // nNormals + pShape->m_nReps * (3 * sizeof(float)) // normals + sizeof(DWORD) // nFaceNormals + (pShape->m_nFaces + pShape->m_nFaceIndices) * sizeof(DWORD); // faceNormals PBYTE pbNormalsCurr = pbNormalsData = new BYTE[cbNormalsSize]; if (pbNormalsData == NULL) { hr = E_OUTOFMEMORY; cout << "AddNormals(): Could not allocate memory for pbNormalsData." << endl; goto e_Exit; } // nNormals WRITE_DWORD(pbNormalsCurr, ((DWORD)pShape->m_nReps)); // normals UINT iRep; for (iRep = 0; iRep < pShape->m_nReps; iRep++) { if (pShape->m_aReps[iRep].m_iNorm == -1) // no normal index found { WRITE_FLOAT(pbNormalsCurr, 1.0f); WRITE_FLOAT(pbNormalsCurr, 0.0f); WRITE_FLOAT(pbNormalsCurr, 0.0f); } else { WRITE_FLOAT(pbNormalsCurr, pShape->m_aNormals[pShape->m_aReps[iRep].m_iNorm].vec[0]); WRITE_FLOAT(pbNormalsCurr, pShape->m_aNormals[pShape->m_aReps[iRep].m_iNorm].vec[1]); WRITE_FLOAT(pbNormalsCurr, pShape->m_aNormals[pShape->m_aReps[iRep].m_iNorm].vec[2]); } } // nFaceNormals WRITE_DWORD(pbNormalsCurr, ((DWORD)pShape->m_nFaces)); // faceNormals UINT iFace; for (iFace = 0; iFace < pShape->m_nFaces; iFace++) { WRITE_DWORD(pbNormalsCurr, ((DWORD)pShape->m_aFaces[iFace].m_nIndices)); for (UINT iIndex = 0; iIndex < pShape->m_aFaces[iFace].m_nIndices; iIndex++) WRITE_DWORD(pbNormalsCurr, ((DWORD)pShape->m_aFaces[iFace].m_aIndices[iIndex])); } hr = pxofSave->CreateDataObject(TID_D3DRMMeshNormals, NULL, NULL, cbNormalsSize, pbNormalsData, &pNormalsDataObject); if (FAILED(hr)) { cout << "AddNormals(): Could not create normals data object." << endl; goto e_Exit; } hr = pShapeDataObject->AddDataObject(pNormalsDataObject); if (FAILED(hr)) { cout << "AddNormals(): Could not add pNormalsDataObject to pShapeDataObject." << endl; goto e_Exit; } e_Exit: // clean up delete[] pbNormalsData; if (pNormalsDataObject) pNormalsDataObject->Release(); return hr; } HRESULT AddTexCoords ( SMesh* pShape, LPDIRECTXFILEDATA pShapeDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; UINT iRep; LPDIRECTXFILEDATA pTexCoordsDataObject = NULL; PBYTE pbTexCoordsData = NULL; int cbTexCoordsSize = sizeof(DWORD) // nTextureCoords + pShape->m_nReps * (2 * sizeof(float)); // textureCoords PBYTE pbTexCoordsCurr = pbTexCoordsData = new BYTE[cbTexCoordsSize]; if (pbTexCoordsData == NULL) { hr = E_OUTOFMEMORY; cout << "AddTexCoords(): Could not allocate memory for pbTexCoordsData." << endl; goto e_Exit; } // nTextureCoords WRITE_DWORD(pbTexCoordsCurr, ((DWORD)pShape->m_nReps)); // textureCoords for (iRep = 0; iRep < pShape->m_nReps; iRep++) { if (pShape->m_aReps[iRep].m_iUV == -1) { WRITE_FLOAT(pbTexCoordsCurr, 0.0f); WRITE_FLOAT(pbTexCoordsCurr, 0.0f); } else { WRITE_FLOAT(pbTexCoordsCurr, g_iFlipU * pShape->m_aUVs[pShape->m_aReps[iRep].m_iUV].vec[0]); WRITE_FLOAT(pbTexCoordsCurr, g_iFlipV * pShape->m_aUVs[pShape->m_aReps[iRep].m_iUV].vec[1]); } } hr = pxofSave->CreateDataObject(TID_D3DRMMeshTextureCoords, NULL, NULL, cbTexCoordsSize, pbTexCoordsData, &pTexCoordsDataObject); if (FAILED(hr)) { cout << "AddTexCoords(): Could not create pTexCoordsDataObject." << endl; goto e_Exit; } hr = pShapeDataObject->AddDataObject(pTexCoordsDataObject); if (FAILED(hr)) { cout << "AddTexCoords(): Could not add data object." << endl; goto e_Exit; } e_Exit: delete[] pbTexCoordsData; if (pTexCoordsDataObject) pTexCoordsDataObject->Release(); return hr; } HRESULT AddMaterialList ( SMesh* pShape, LPDIRECTXFILEDATA pShapeDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; UINT iGroup, iFace; LPDIRECTXFILEDATA pMaterialsDataObject = NULL; PBYTE pbMaterialsData = NULL; int cbMaterialsSize = sizeof(DWORD) // nMaterials + sizeof(DWORD) // nFaceIndexes + pShape->m_nFaces * sizeof(DWORD); // FaceIndexes PBYTE pbMaterialsCurr = pbMaterialsData = new BYTE[cbMaterialsSize]; if (pbMaterialsCurr == NULL) { hr = E_OUTOFMEMORY; cout << "AddMaterialList(): Could not allocate memory for pbMaterialsData." << endl; goto e_Exit; } // nMaterials WRITE_DWORD(pbMaterialsCurr, ((DWORD)pShape->m_nGroups)); // nFaceIndexes WRITE_DWORD(pbMaterialsCurr, ((DWORD)pShape->m_nFaces)); // FaceIndexes for (iFace = 0; iFace < pShape->m_nFaces; iFace++) WRITE_DWORD(pbMaterialsCurr, pShape->m_aFaces[iFace].m_iGroup); hr = pxofSave->CreateDataObject(TID_D3DRMMeshMaterialList, NULL, NULL, cbMaterialsSize, pbMaterialsData, &pMaterialsDataObject); if (FAILED(hr)) { cout << "AddMaterialList(): Could not create pMaterialsDataObject." << endl; goto e_Exit; } // material data for (iGroup = 0; iGroup < pShape->m_nGroups; iGroup++) { LPDIRECTXFILEDATA pMaterialDataObject = NULL; LPDIRECTXFILEDATA pTextureDataObject = NULL; PBYTE pbMaterialData = NULL; int cbMaterialSize = 4 * sizeof(float) // faceColor + sizeof(float) // power + 3 * sizeof(float) // specularColor + 3 * sizeof(float); // emissiveColor PBYTE pbMaterialCurr = pbMaterialData = new BYTE[cbMaterialSize]; if (pbMaterialData == NULL) { hr = E_OUTOFMEMORY; cout << "AddMaterialList(): Could not allocate memory for pbMaterialData." << endl; goto e_Exit2; } // faceColor WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fDiffuseRed); WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fDiffuseGreen); WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fDiffuseBlue); WRITE_FLOAT(pbMaterialCurr, (1.0f - pShape->m_aGroups[iGroup].m_fTransparency)); // power WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fShininess); // specularColor WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fSpecularRed); WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fSpecularGreen); WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fSpecularBlue); // emissiveColor WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fEmissiveRed); WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fEmissiveGreen); WRITE_FLOAT(pbMaterialCurr, pShape->m_aGroups[iGroup].m_fEmissiveBlue); hr = pxofSave->CreateDataObject(TID_D3DRMMaterial, pShape->m_aGroups[iGroup].m_szName, NULL, cbMaterialSize, pbMaterialData, &pMaterialDataObject); if (FAILED(hr)) { cout << "AddMaterialList(): Could not create pMaterialDataObject." << endl; goto e_Exit2; } // TextureFilename if (pShape->m_aGroups[iGroup].m_szTextureFile != NULL) { int cbTextureSize = sizeof(char**); hr = pxofSave->CreateDataObject(TID_D3DRMTextureFilename, NULL, NULL, cbTextureSize, &pShape->m_aGroups[iGroup].m_szTextureFile, &pTextureDataObject); if (FAILED(hr)) { cout << "AddMaterialList(): Could not create pMaterialDataObject." << endl; goto e_Exit2; } hr = pMaterialDataObject->AddDataObject(pTextureDataObject); if (FAILED(hr)) { cout << "AddMaterialList(): Could not add pTextureDataObject to pMaterialDataObject." << endl; goto e_Exit2; } } hr = pMaterialsDataObject->AddDataObject(pMaterialDataObject); if (FAILED(hr)) { cout << "AddMaterialList(): Could not add pMaterialDataObject to pMaterialsDataObject." << endl; goto e_Exit2; } e_Exit2: delete[] pbMaterialData; if (pMaterialDataObject) pMaterialDataObject->Release(); if (pTextureDataObject) pTextureDataObject->Release(); if (FAILED(hr)) { cout << "AddMaterialList(): Error occured while adding materials." << endl; goto e_Exit; } } hr = pShapeDataObject->AddDataObject(pMaterialsDataObject); if (FAILED(hr)) { cout << "AddMaterialList(): Could not add pMaterialsDataObject to pShapeDataObject." << endl; goto e_Exit; } e_Exit: delete[] pbMaterialsData; if (pMaterialsDataObject) pMaterialsDataObject->Release(); return hr; } HRESULT AddVertexColors ( SMesh* pShape, LPDIRECTXFILEDATA pShapeDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; LPDIRECTXFILEDATA pColorsDataObject = NULL; PBYTE pbColorsData = NULL; UINT iRep; UINT cbColorsSize = sizeof(DWORD) // nVertexColors + pShape->m_nReps * (sizeof(DWORD) + 4 * sizeof(float)); // vertexColors PBYTE pbColorsCurr = pbColorsData = new BYTE[cbColorsSize]; if (NULL == pbColorsData) { hr = E_OUTOFMEMORY; cout << "AddVertexColors(): Could not allocate memory for pbColorsData." << endl; goto e_Exit; } // nVertexColors WRITE_DWORD(pbColorsCurr, ((DWORD)pShape->m_nReps)); // vertexColors for (iRep = 0; iRep < pShape->m_nReps; iRep++) { // index WRITE_DWORD(pbColorsCurr, ((DWORD)iRep)); // indexedColor WRITE_FLOAT(pbColorsCurr, pShape->m_aVertexColors[pShape->m_aReps[iRep].m_iFirst].r); // red WRITE_FLOAT(pbColorsCurr, pShape->m_aVertexColors[pShape->m_aReps[iRep].m_iFirst].g); // green WRITE_FLOAT(pbColorsCurr, pShape->m_aVertexColors[pShape->m_aReps[iRep].m_iFirst].b); // blue WRITE_FLOAT(pbColorsCurr, pShape->m_aVertexColors[pShape->m_aReps[iRep].m_iFirst].a); // alpha } hr = pxofSave->CreateDataObject(TID_D3DRMMeshVertexColors, NULL, NULL, cbColorsSize, pbColorsData, &pColorsDataObject); if (FAILED(hr)) { cout << "AddVertexColors(): Could not create pColorsDataObject." << endl; goto e_Exit; } hr = pShapeDataObject->AddDataObject(pColorsDataObject); if (FAILED(hr)) { cout << "AddVertexColors(): Could not add pColorsDataObject to pShapeDataObject." << endl; goto e_Exit; } e_Exit: delete[] pbColorsData; if (pColorsDataObject) pColorsDataObject->Release(); return hr; } HRESULT AddRepInfo ( SMesh* pShape, LPDIRECTXFILEDATA pShapeDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; LPDIRECTXFILEDATA pRepsDataObject = NULL; PBYTE pbRepsData= NULL; UINT iRep; UINT cbRepsSize = sizeof(DWORD) // nIndices + sizeof(DWORD) // nOriginalVertices + pShape->m_nReps * sizeof(DWORD); // indices PBYTE pbRepsCurr = pbRepsData = new BYTE[cbRepsSize]; if (NULL == pbRepsData) { hr = E_OUTOFMEMORY; cout << "AddRepInfo(): Could not allocate memory for pbRepsData." << endl; goto e_Exit; } // nIndices WRITE_DWORD(pbRepsCurr, ((DWORD)pShape->m_nReps)); // nOriginalVertices WRITE_DWORD(pbRepsCurr, ((DWORD)pShape->m_nPoints)); // indices for (iRep = 0; iRep < pShape->m_nReps; iRep++) WRITE_DWORD(pbRepsCurr, ((DWORD)pShape->m_aReps[iRep].m_iFirst)); hr = pxofSave->CreateDataObject(DXFILEOBJ_VertexDuplicationIndices, NULL, NULL, cbRepsSize, pbRepsData, &pRepsDataObject); if (FAILED(hr)) { cout << "AddRepInfo(): Could not create pRepsDataObject." << endl; goto e_Exit; } hr = pShapeDataObject->AddDataObject(pRepsDataObject); if (FAILED(hr)) { cout << "AddRepInfo(): Could not add pRepsDataObject to pShapeDataObject." << endl; goto e_Exit; } e_Exit: delete[] pbRepsData; if (pRepsDataObject) pRepsDataObject->Release(); return hr; } HRESULT AddPatchMesh ( SMesh* pShape, LPDIRECTXFILEDATA pFrameDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; LPDIRECTXFILEDATA pShapeDataObject = NULL; PBYTE pbShapeData = NULL; UINT iFace, iRep; // counters UINT cbShapeSize = sizeof(DWORD) // nVertices + pShape->m_nReps * (3 * sizeof(float)) // vertices + sizeof(DWORD) // nPatches + (pShape->m_nFaces + pShape->m_nFaceIndices) * sizeof(DWORD); // patches PBYTE pbShapeCurr = pbShapeData = new BYTE[cbShapeSize]; if (NULL == pbShapeData) { hr = E_OUTOFMEMORY; cout << "AddPatchMesh(): Could not allocate memory for pbpShapeData." << endl; goto e_Exit; } // nVertices WRITE_DWORD(pbShapeCurr, ((DWORD)pShape->m_nPoints)); // vertices for (iRep = 0; iRep < pShape->m_nReps; iRep++) { WRITE_FLOAT(pbShapeCurr, pShape->m_aPoints[pShape->m_aReps[iRep].m_iFirst].vec[0]); WRITE_FLOAT(pbShapeCurr, pShape->m_aPoints[pShape->m_aReps[iRep].m_iFirst].vec[1]); WRITE_FLOAT(pbShapeCurr, pShape->m_aPoints[pShape->m_aReps[iRep].m_iFirst].vec[2]); } // nPatches WRITE_DWORD(pbShapeCurr, ((DWORD)pShape->m_nFaces)); // faces for (iFace = 0; iFace < pShape->m_nFaces; iFace++) { WRITE_DWORD(pbShapeCurr, ((DWORD)16)); for (UINT iIndex = 0; iIndex < 16; iIndex++) WRITE_DWORD(pbShapeCurr, pShape->m_aFaces[iFace].m_aIndices[iIndex]); } hr = pxofSave->CreateDataObject(DXFILEOBJ_PatchMesh, NULL, NULL, cbShapeSize, pbShapeData, &pShapeDataObject); if (FAILED(hr)) { cout << "AddPatchMesh(): Could not create pShapeDataObject." << endl; goto e_Exit; } // MeshTextureCoords if (pShape->m_nUVs > 0) { hr = AddTexCoords(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPatchMesh(): Could not add texture coordinate info." << endl; goto e_Exit; } } // MeshVertexColors // hr = AddVertexColors(pShape, pShapeDataObject, pxofSave), // if (FAILED(hr)) // { // cout << "AddPatchMesh(): Could not add vertex color info." << endl; // goto e_Exit; // } // MeshMaterialList hr = AddMaterialList(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPatchMesh(): Could not add materials info." << endl; goto e_Exit; } // VertexDuplicationIndices hr = AddRepInfo(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPatchMesh(): Could not add rep info." << endl; goto e_Exit; } // XSkinMeshHeader if (pShape->m_nBones > 0) { hr = AddSkin(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPatchMesh(): Could not add skin info." << endl; goto e_Exit; } } hr = pFrameDataObject->AddDataObject(pShapeDataObject); if (FAILED(hr)) { cout << "AddPatchMesh(): Could not add pShapeDataObject to pFrameDataObject." << endl; goto e_Exit; } e_Exit: delete[] pbShapeData; if (pShapeDataObject) pShapeDataObject->Release(); return hr; } HRESULT AddPolyMesh ( SMesh* pShape, LPDIRECTXFILEDATA pFrameDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { HRESULT hr = S_OK; LPDIRECTXFILEDATA pShapeDataObject = NULL; PBYTE pbMeshData = NULL; UINT iFace, iRep; // counters UINT cbMeshSize = sizeof(DWORD) // nVertices + pShape->m_nReps * (3 * sizeof(float)) // vertices + sizeof(DWORD) // nFaces + (pShape->m_nFaces + pShape->m_nFaceIndices) * sizeof(DWORD); // faces PBYTE pbMeshCurr = pbMeshData = new BYTE[cbMeshSize]; if (NULL == pbMeshData) { hr = E_OUTOFMEMORY; cout << "AddPolyMesh(): Could not allocate memory for pbMeshData." << endl; goto e_Exit; } // nVertices WRITE_DWORD(pbMeshCurr, ((DWORD)pShape->m_nReps)); // vertices for (iRep = 0; iRep < pShape->m_nReps; iRep++) { WRITE_FLOAT(pbMeshCurr, pShape->m_aPoints[pShape->m_aReps[iRep].m_iFirst].vec[0]); WRITE_FLOAT(pbMeshCurr, pShape->m_aPoints[pShape->m_aReps[iRep].m_iFirst].vec[1]); WRITE_FLOAT(pbMeshCurr, pShape->m_aPoints[pShape->m_aReps[iRep].m_iFirst].vec[2]); } // nFaces WRITE_DWORD(pbMeshCurr, ((DWORD)pShape->m_nFaces)); // faces for (iFace = 0; iFace < pShape->m_nFaces; iFace++) { WRITE_DWORD(pbMeshCurr, ((DWORD)pShape->m_aFaces[iFace].m_nIndices)); for (UINT iIndex = 0; iIndex < pShape->m_aFaces[iFace].m_nIndices; iIndex++) WRITE_DWORD(pbMeshCurr, ((DWORD)pShape->m_aFaces[iFace].m_aIndices[iIndex])); } hr = pxofSave->CreateDataObject(TID_D3DRMMesh, NULL, NULL, cbMeshSize, pbMeshData, &pShapeDataObject); if (FAILED(hr)) { cout << "AddPolyMesh(): Could not create pShapeDataObject." << endl; goto e_Exit; } // MeshNormals if (pShape->m_nNormals > 0) { hr = AddNormals(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPolyMesh(): Could not add normal info." << endl; goto e_Exit; } } // MeshTextureCoords if (pShape->m_nUVs > 0) { hr = AddTexCoords(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPolyMesh(): Could not add texture coordinate info." << endl; goto e_Exit; } } // MeshVertexColors // hr = AddVertexColors(pShape, pShapeDataObject, pxofSave); // if (FAILED(hr)) // { // cout << "AddPolyMesh(): Could not add vertex color info." << endl; // goto e_Exit; // } // MeshMaterialList hr = AddMaterialList(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPolyMesh(): Could not add materials info." << endl; goto e_Exit; } // VertexDuplicationIndices hr = AddRepInfo(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPolyMesh(): Could not add rep info." << endl; goto e_Exit; } // XSkinMeshHeader if (pShape->m_nBones > 0) { hr = AddSkin(pShape, pShapeDataObject, pxofSave); if (FAILED(hr)) { cout << "AddPolyMesh(): Could not add skin info." << endl; goto e_Exit; } } hr = pFrameDataObject->AddDataObject(pShapeDataObject); if (FAILED(hr)) { cout << "AddPolyMesh(): Could not add pShapeDataObject to pFrameDataObject." << endl; goto e_Exit; } e_Exit: delete[] pbMeshData; if (pShapeDataObject) pShapeDataObject->Release(); return hr; } HRESULT AddShape ( MDagPath& mdpTransform, bool bParentVisibility, LPDIRECTXFILEDATA pParentFrameDataObject, LPDIRECTXFILESAVEOBJECT pxofSave ) { MStatus stat; UINT i, j; HRESULT hr = S_OK; MItDag itDag; MFnTransform fnTransform; char* szName; float* afLocalTransform; bool bVisibility, bLodVisibility, bOverrideEnabled, bOverrideVisibility; bool bIsVisible; LPDIRECTXFILEDATA pFrameDataObject = NULL; LPDIRECTXFILEDATA pMatrixDataObject = NULL; if (!mdpTransform.hasFn(MFn::kTransform)) { cout << "AddShape(): Object at DAG path was not a transform." << endl; goto e_Exit; } stat = fnTransform.setObject(mdpTransform.node()); if (!stat) { cout << "AddShape(): Could not read transform object." << endl; goto e_Exit; } // check if mesh is visible bVisibility = true; bLodVisibility = true; bOverrideEnabled = false; bOverrideVisibility = true; do // ONCE { MPlug mpVisibility, mpLodVisibility, mpOverrideEnabled, mpOverrideVisibility; mpVisibility = fnTransform.findPlug("visibility", &stat); if (!stat) break; stat = mpVisibility.getValue(bVisibility); if (!stat) break; mpLodVisibility = fnTransform.findPlug("lodVisibility", &stat); if (!stat) break; stat = mpLodVisibility.getValue(bLodVisibility); if (!stat) break; mpOverrideEnabled = fnTransform.findPlug("overrideEnabled", &stat); if (!stat) break; stat = mpOverrideEnabled.getValue(bOverrideEnabled); if (!stat) break; mpOverrideVisibility = fnTransform.findPlug("overrideVisibility", &stat); if (!stat) break; stat = mpOverrideVisibility.getValue(bOverrideVisibility); if (!stat) break; } while (false); bIsVisible = bParentVisibility && bLodVisibility && (!bOverrideEnabled || bOverrideVisibility); // shape name szName = NULL; szName = new char[1 + mdpTransform.partialPathName().length()]; if (szName == NULL) { hr = E_OUTOFMEMORY; cout << "AddShape(): Could not allocate memory for szName." << endl; goto e_Exit; } strcpy(szName, mdpTransform.partialPathName().asChar()); for (i = 0; szName[i] != '\0'; i++) // maya names may contain '|' or perhaps even ' ' { if (szName[i] == ' ' || szName[i] == '|') szName[i] = '_'; } hr = g_Arrays.Add(STRING, szName); if (FAILED(hr)) goto e_Exit; cout << "\tReading \"" << mdpTransform.fullPathName().asChar() << "\"..."; // local transform afLocalTransform = NULL; afLocalTransform = new float[16]; if (afLocalTransform == NULL) { hr = E_OUTOFMEMORY; cout << "AddShape(): Could not allocate memory for afLocalTransform." << endl; goto e_Exit; } for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { afLocalTransform[i * 4 + j] = (float)fnTransform.transformation().asMatrix()[i][j]; } } hr = g_Arrays.Add(FLOAT_ARRAY, afLocalTransform); if (FAILED(hr)) goto e_Exit; cout << "TRANSFORM"; // Frame hr = pxofSave->CreateDataObject(TID_D3DRMFrame, szName, NULL, 0, NULL, &pFrameDataObject); if (FAILED(hr)) { cout << "AddShape(): Could not create pFrameDataObject." << endl; goto e_Exit; } // FrameTransformMatrix hr = pxofSave->CreateDataObject(TID_D3DRMFrameTransformMatrix, NULL, NULL, 16 * sizeof(float), afLocalTransform, &pMatrixDataObject); if (FAILED(hr)) { cout << "AddShape(): Could not create pMatrixDataObject." << endl; goto e_Exit; } hr = pFrameDataObject->AddDataObject(pMatrixDataObject); if (FAILED(hr)) { cout << "AddShape(): Could not add pMatrixDataObject to pFrameDataObject." << endl; goto e_Exit; } // the reason for the braces here is to place Mesh in a local scope so that will be deleted quickly if (mdpTransform.hasFn(MFn::kJoint)) { // bone cout << "/BONE" << endl; } else if (bIsVisible && mdpTransform.hasFn(MFn::kMesh)) { // poly mesh SMesh Mesh; MDagPath mdpMesh = mdpTransform; cout << "(MESH)" << endl; stat = mdpMesh.extendToShape(); if (!stat) { hr = E_FAIL; cout << "AddShape(): Could not extend to shape node." << endl; goto e_Exit; } hr = LoadPolyMesh(mdpTransform, mdpMesh, &Mesh); if (FAILED(hr)) { cout << "AddShape(): Could not load poly-mesh." << endl; goto e_Exit; } if (Mesh.m_kType == SMesh::POLY_MESH) { hr = AddPolyMesh(&Mesh, pFrameDataObject, pxofSave); if (FAILED(hr)) { cout << "AddShape(): Could not add mesh" << endl; goto e_Exit; } } } else if (bIsVisible && mdpTransform.hasFn(MFn::kNurbsSurface)) { SMesh Mesh; MDagPath mdpNurbs = mdpTransform; cout << "(NURBS) (ignored)" << endl; stat = mdpNurbs.extendToShape(); if (!stat) { hr = E_FAIL; cout << "AddShape(): Could not extend to shape node." << endl; goto e_Exit; } // hr = LoadPatchMesh(mdpTransform, mdpNurbs, &Mesh); if (FAILED(hr)) { cout << "AddShape(): Could not load patch-mesh." << endl; goto e_Exit; } if (Mesh.m_kType == SMesh::POLY_MESH) { hr = AddPolyMesh(&Mesh, pFrameDataObject, pxofSave); if (FAILED(hr)) { cout << "AddShape(): Could not add tesselated patch-mesh" << endl; goto e_Exit; } } else if (Mesh.m_kType == SMesh::PATCH_MESH) { hr = AddPatchMesh(&Mesh, pFrameDataObject, pxofSave); if (FAILED(hr)) { cout << "AddShape(): Could not add patch-mesh" << endl; goto e_Exit; } } } else { cout << endl; } // add children itDag.reset(mdpTransform.node(), MItDag::kBreadthFirst, MFn::kTransform); itDag.next(); for (; !itDag.isDone() && itDag.depth() == 1; itDag.next()) { MDagPath mdpChild; itDag.getPath(mdpChild); #ifdef NO_INTERMEDIATE_OBJECTS if (MFnDagNode(mdpChild).isIntermediateObject()) continue; #endif hr = AddShape(mdpChild, bIsVisible, pFrameDataObject, pxofSave); if (FAILED(hr)) cout << "AddShape(): Could not add child shape. Ignoring..." << endl; } hr = pParentFrameDataObject->AddDataObject(pFrameDataObject); if (FAILED(hr)) { cout << "AddShape(): Could not add pFrameDataObject to pParentFrameDataObject." << endl; goto e_Exit; } g_AddedPaths.append(mdpTransform); e_Exit: if (pMatrixDataObject) pMatrixDataObject->Release(); if (pFrameDataObject) pFrameDataObject->Release(); return hr; } #define SWAP(array, i1, i2) { UINT temp = (array)[(i1)]; (array)[(i1)] = (array)[(i2)]; (array)[(i2)] = temp; } HRESULT LoadKeyframedAnims ( UINT* pnAnims, SAnim** paAnims ) { HRESULT hr = S_OK; MStatus stat = MS::kSuccess; MAnimControl mAnimCtrl; MFnTransform fnTransform; SAnim* aAnims = NULL; UINT nFPS; // num frames per second float fTimeFactor; UINT nAnims = 0, nPending, nActive; UINT iAnim, iPath, iPlug, iCurve, iKey, iPending, iRow, iCol; // counters UINT* aiCurKey = NULL; UINT* aiIndices = NULL; MIntArray AnimPaths; float afMatrix[16]; char* pcAnim; char* szAnim = NULL; MTime mtMinTime, mtOriginalTime; MTimeArray* amatTimes = NULL; mtOriginalTime = mAnimCtrl.currentTime(); // calculate the frames per second switch(MTime::uiUnit()) { case MTime::kSeconds: // 1 fps nFPS = 1; break; case MTime::kMilliseconds: // 1000 fps nFPS = 1000; break; case MTime::kGames: // 15 fps nFPS = 15; break; case MTime::kFilm: // 24 fps nFPS = 24; break; case MTime::kPALFrame: // 25 fps nFPS = 25; break; case MTime::kNTSCFrame: // 30 fps nFPS = 30; break; case MTime::kShowScan: // 48 fps nFPS = 48; break; case MTime::kPALField: // 50 fps nFPS = 50; break; case MTime::kNTSCField: // 60 fps nFPS = 60; break; default: nFPS = 1; break; }; fTimeFactor = 3600.0f / (float)nFPS; // find animated objects for (iPath = 0; iPath < g_AddedPaths.length(); iPath++) { MFnTransform fnTransform; stat = fnTransform.setObject(g_AddedPaths[iPath]); if (!stat) continue; if (!MAnimUtil::isAnimated(g_AddedPaths[iPath], false)) continue; AnimPaths.append(iPath); } nAnims = AnimPaths.length(); if (0 == nAnims) goto e_Exit; amatTimes = new MTimeArray[nAnims]; if (NULL == amatTimes) { hr = E_OUTOFMEMORY; cout << "LoadKeyframedAnims(): Could not allocate times array." << endl; goto e_Exit; } aAnims = new SAnim[nAnims]; if (NULL == aAnims) { hr = E_OUTOFMEMORY; cout << "LoadKeyframedAnims(): Could not allocate anim array." << endl; goto e_Exit; } aiCurKey = new UINT[nAnims]; if (NULL == aiCurKey) { hr = E_OUTOFMEMORY; cout << "LoadKeyframedAnims(): Could not allocate current-key array." << endl; goto e_Exit; } aiIndices = new UINT[nAnims]; if (NULL == aiIndices) { hr = E_OUTOFMEMORY; cout << "LoadKeyframedAnims(): Could not allocate indirection array." << endl; goto e_Exit; } // consolidate individual anim curves for each animated path for (nActive = 0, nPending = 0, iAnim = 0; iAnim < nAnims; iAnim++) { MPlugArray AnimPlugs; aiCurKey[iAnim] = 0; aiIndices[iAnim] = iAnim; MAnimUtil::findAnimatedPlugs(g_AddedPaths[AnimPaths[iAnim]], AnimPlugs, false, &stat); if (!stat) { cout << "LoadKeyframedAnims(): Ignoring animation because could not find animated plugs." << endl; continue; } for (iPlug = 0; iPlug < AnimPlugs.length(); iPlug++) { MObjectArray Curves; MAnimUtil::findAnimation(AnimPlugs[iPlug], Curves, &stat); if (!stat) { cout << "LoadKeyframedAnims(): Ignoring anim-plug because could not find animation." << endl; continue; } for (iCurve = 0; iCurve < Curves.length(); iCurve++) { UINT iTime; MFnAnimCurve fnCurve(Curves[iCurve], &stat); if (!stat) { cout << "LoadKeyframedAnims(): Ignoring anim-curve because it could not be read with anim-curve function set." << endl; continue; } for (iTime = 0, iKey = 0; iKey < fnCurve.numKeys(); iKey++) { MTime mtTime = fnCurve.time(iKey, &stat); if (!stat) { cout << "LoadKeyframedAnims(): Ignoring anim-curve-key because it could not be read." << endl; continue; } for (; iTime < amatTimes[iAnim].length() && mtTime > amatTimes[iAnim][iTime]; iTime++); if (iTime < amatTimes[iAnim].length() && mtTime < amatTimes[iAnim][iTime]) { amatTimes[iAnim].insert(mtTime, iTime); } else if (iTime == amatTimes[iAnim].length()) { amatTimes[iAnim].append(mtTime); } } // loop through keys } // loop through curves } // loop through plugs // get name delete[] szAnim; szAnim = NULL; szAnim = new char[g_AddedPaths[AnimPaths[iAnim]].partialPathName().length() + 1]; if (NULL == szAnim) { hr = E_OUTOFMEMORY; cout << "AddAnimation(): Could not allocate string for anim name." << endl; goto e_Exit; } strcpy(szAnim, g_AddedPaths[AnimPaths[iAnim]].partialPathName().asChar()); for (pcAnim = szAnim; *pcAnim != '\0'; pcAnim++) { if (*pcAnim == ' ' || *pcAnim == '|') *pcAnim = '_'; } aAnims[iAnim].m_szName = szAnim; g_Arrays.Add(STRING, aAnims[iAnim].m_szName); szAnim = NULL; aAnims[iAnim].m_nKeys = amatTimes[iAnim].length(); if (0 == aAnims[iAnim].m_nKeys) continue; aAnims[iAnim].m_aKeys = new SKey[aAnims[iAnim].m_nKeys]; if (NULL == aAnims[iAnim].m_aKeys) { hr = E_OUTOFMEMORY; cout << "LoadKeyframedAnims(): Could not allocate anim-keys." << endl; goto e_Exit; } SWAP(aiIndices, iAnim, nPending); nPending++; } // loop through animated paths while (nPending + nActive > 0) { iPending = 0; while (iPending < nPending) { iAnim = aiIndices[iPending]; if (0 == nActive || amatTimes[iAnim][aiCurKey[iAnim]] <= mtMinTime) { mtMinTime = amatTimes[iAnim][aiCurKey[iAnim]]; nPending--; nActive++; SWAP(aiIndices, iPending, nPending); SWAP(aiIndices, nPending, nAnims - nActive); continue; } iPending++; } mAnimCtrl.setCurrentTime(mtMinTime); // set time to mtMinTime while (nActive > 0) { iAnim = aiIndices[nAnims - nActive]; if (mtMinTime < amatTimes[iAnim][aiCurKey[iAnim]]) { mtMinTime = amatTimes[iAnim][aiCurKey[iAnim]]; break; } // read matrix stat = fnTransform.setObject(g_AddedPaths[AnimPaths[iAnim]]); if (!stat) { hr = E_FAIL; cout << "LoadKeyframedAnims(): Could not read transform object using function set. Aborting..." << endl; goto e_Exit; } for (iRow = 0; iRow < 4; iRow++) for (iCol = 0; iCol < 4; iCol++) afMatrix[iRow * 4 + iCol] = (float)fnTransform.transformation().asMatrix()[iRow][iCol]; aAnims[iAnim].m_aKeys[aiCurKey[iAnim]].m_iFrame = (UINT)((float)mtMinTime.value() * fTimeFactor); if (!DtMatrixGetTransforms(afMatrix, aAnims[iAnim].m_aKeys[aiCurKey[iAnim]].m_afPosition, aAnims[iAnim].m_aKeys[aiCurKey[iAnim]].m_afScale, aAnims[iAnim].m_aKeys[aiCurKey[iAnim]].m_afQuaternion, aAnims[iAnim].m_aKeys[aiCurKey[iAnim]].m_afRotation)) { cout << "LoadKeyframedAnims(): Error in getting TRS components." << endl; goto e_Exit; } aAnims[iAnim].m_aKeys[aiCurKey[iAnim]].m_afQuaternion[0] = -aAnims[iAnim].m_aKeys[aiCurKey[iAnim]].m_afQuaternion[0]; aiCurKey[iAnim]++; if (aiCurKey[iAnim] < aAnims[iAnim].m_nKeys) { SWAP(aiIndices, nAnims - nActive, nPending); nPending++; } nActive--; } } e_Exit: mAnimCtrl.setCurrentTime(mtOriginalTime); if (FAILED(hr)) { delete[] aAnims; nAnims = 0; aAnims = NULL; } delete[] amatTimes; delete[] aiCurKey; delete[] aiIndices; delete[] szAnim; *pnAnims = nAnims; *paAnims = aAnims; return hr; } HRESULT LoadFixedStepAnims ( UINT* pnAnims, SAnim** paAnims ) { HRESULT hr = S_OK; MStatus stat = MS::kSuccess; MAnimControl mAnimCtrl; MFnTransform fnTransform; SAnim* aAnims = NULL; UINT nFPS; // num frames per second float fTimeFactor; UINT nAnims = 0; UINT iAnim, iPath, iRow, iCol, iKey; // counters MIntArray AnimPaths; float afMatrix[16]; char* pcAnim; char* szAnim = NULL; MTimeArray matKeys; MTime mtKey, mtMinTime, mtOriginalTime; mtOriginalTime = mAnimCtrl.currentTime(); // calculate the frames per second switch(MTime::uiUnit()) { case MTime::kSeconds: // 1 fps nFPS = 1; break; case MTime::kMilliseconds: // 1000 fps nFPS = 1000; break; case MTime::kGames: // 15 fps nFPS = 15; break; case MTime::kFilm: // 24 fps nFPS = 24; break; case MTime::kPALFrame: // 25 fps nFPS = 25; break; case MTime::kNTSCFrame: // 30 fps nFPS = 30; break; case MTime::kShowScan: // 48 fps nFPS = 48; break; case MTime::kPALField: // 50 fps nFPS = 50; break; case MTime::kNTSCField: // 60 fps nFPS = 60; break; default: nFPS = 1; break; }; fTimeFactor = 3600.0f / (float)nFPS; for (mtKey = MAnimControl::minTime(); mtKey <= MAnimControl::maxTime(); mtKey += g_iFrameStep) matKeys.append(mtKey); // find animated objects for (iPath = 0; iPath < g_AddedPaths.length(); iPath++) { MFnTransform fnTransform; stat = fnTransform.setObject(g_AddedPaths[iPath]); if (!stat) continue; if (!g_bAnimateEverything && !MAnimUtil::isAnimated(g_AddedPaths[iPath], false)) continue; AnimPaths.append(iPath); } nAnims = AnimPaths.length(); if (0 == nAnims) goto e_Exit; aAnims = new SAnim[nAnims]; if (NULL == aAnims) { hr = E_OUTOFMEMORY; cout << "LoadFixedStepAnims(): Could not allocate anim array." << endl; goto e_Exit; } // consolidate individual anim curves for each animated path for (iAnim = 0; iAnim < nAnims; iAnim++) { // get name delete[] szAnim; szAnim = NULL; szAnim = new char[g_AddedPaths[AnimPaths[iAnim]].partialPathName().length() + 1]; if (NULL == szAnim) { hr = E_OUTOFMEMORY; cout << "AddAnimation(): Could not allocate string for anim name." << endl; goto e_Exit; } strcpy(szAnim, g_AddedPaths[AnimPaths[iAnim]].partialPathName().asChar()); for (pcAnim = szAnim; *pcAnim != '\0'; pcAnim++) { if (*pcAnim == ' ' || *pcAnim == '|') *pcAnim = '_'; } aAnims[iAnim].m_szName = szAnim; g_Arrays.Add(STRING, aAnims[iAnim].m_szName); szAnim = NULL; aAnims[iAnim].m_nKeys = matKeys.length(); if (0 == aAnims[iAnim].m_nKeys) continue; aAnims[iAnim].m_aKeys = new SKey[aAnims[iAnim].m_nKeys]; if (NULL == aAnims[iAnim].m_aKeys) { hr = E_OUTOFMEMORY; cout << "LoadFixedStepAnims(): Could not allocate anim-keys." << endl; goto e_Exit; } } // loop through animated paths for (iKey = 0; iKey < matKeys.length(); iKey++) { mAnimCtrl.setCurrentTime(matKeys[iKey]); // update time for (iAnim = 0; iAnim < nAnims; iAnim++) { // read matrix stat = fnTransform.setObject(g_AddedPaths[AnimPaths[iAnim]]); if (!stat) { hr = E_FAIL; cout << "LoadFixedStepAnims(): Could not read transform object using function set. Aborting..." << endl; goto e_Exit; } for (iRow = 0; iRow < 4; iRow++) for (iCol = 0; iCol < 4; iCol++) afMatrix[iRow * 4 + iCol] = (float)fnTransform.transformation().asMatrix()[iRow][iCol]; aAnims[iAnim].m_aKeys[iKey].m_iFrame = (UINT)((float)matKeys[iKey].value() * fTimeFactor); if (!DtMatrixGetTransforms(afMatrix, aAnims[iAnim].m_aKeys[iKey].m_afPosition, aAnims[iAnim].m_aKeys[iKey].m_afScale, aAnims[iAnim].m_aKeys[iKey].m_afQuaternion, aAnims[iAnim].m_aKeys[iKey].m_afRotation)) { cout << "LoadFixedStepAnims(): Error in getting TRS components." << endl; goto e_Exit; } aAnims[iAnim].m_aKeys[iKey].m_afQuaternion[0] = -aAnims[iAnim].m_aKeys[iKey].m_afQuaternion[0]; } // loop through animated objects } // loop through keys e_Exit: mAnimCtrl.setCurrentTime(mtOriginalTime); if (FAILED(hr)) { delete[] aAnims; nAnims = 0; aAnims = NULL; } delete[] szAnim; *pnAnims = nAnims; *paAnims = aAnims; return hr; } HRESULT AddScene ( const char* szFile ) { HRESULT hr = S_OK; MStatus stat = MS::kSuccess; UINT iPath; MDagPathArray VisiblePaths; LPDIRECTXFILE pxofApi = NULL; LPDIRECTXFILESAVEOBJECT pxofSave = NULL; LPDIRECTXFILEDATA pAnimSetObject = NULL; LPDIRECTXFILEDATA pRootFrameObject = NULL; LPDIRECTXFILEDATA pRootTransformObject = NULL; UINT nAnims, iAnim; SAnim* aAnims = NULL; float rgfIdentity[16] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; MItDag itDag2(MItDag::kDepthFirst, MFn::kTransform); MItDag itIkEffectors(MItDag::kDepthFirst, MFn::kIkEffector); MItDag itDag(MItDag::kBreadthFirst, MFn::kTransform, &stat); if (!stat) { hr = E_FAIL; cout << "AddScene(): Could not traverse nodes." << endl; goto e_Exit; } #ifdef ALWAYS_TESSELATE_NURBS g_bExportPatches = false; #endif // turn off visible objects (this is done to speed up the animation) for (itDag2.reset(); !itDag2.isDone(); itDag2.next()) { MPlug Visibility; MDagPath dagPath; MFnDagNode fnDag; bool bIsVisible; stat = itDag2.getPath(dagPath); if (!stat) continue; stat = fnDag.setObject(dagPath); if (!stat) continue; Visibility = fnDag.findPlug("visibility", &stat); if (!stat) continue; stat = Visibility.getValue(bIsVisible); if (!stat || !bIsVisible) continue; stat = Visibility.setValue(false); if (!stat) continue; VisiblePaths.append(dagPath); } cout << "Exporting to " << szFile << " ..." << endl; // create xofapi object. hr = DirectXFileCreate(&pxofApi); if (FAILED(hr)) { cout << "AddScene(): Could not create xofapi object." << endl; goto e_Exit; } // register templates for d3drm. hr = pxofApi->RegisterTemplates((LPVOID)D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES); if (FAILED(hr)) { cout << "AddScene(): Could not register D3D templates." << endl; goto e_Exit; } // register extra templates for skinning info and vertex duplication hr = pxofApi->RegisterTemplates((LPVOID)XSKINEXP_TEMPLATES, strlen(XSKINEXP_TEMPLATES)); if (FAILED(hr)) { cout << "AddScene(): Could not register Skinning and/or Vertex Duplication templates." << endl; goto e_Exit; } // create save object. hr = pxofApi->CreateSaveObject(szFile, g_FileFormat, &pxofSave); if (FAILED(hr)) { cout << "AddScene(): Could not create save object." << endl; goto e_Exit; } // save templates hr = pxofSave->SaveTemplates(3, g_aIds); if (FAILED(hr)) { cout << "AddScene(): Could not save templates."; goto e_Exit; } // save file data to the file // first create the SCENE_ROOT Frame hr = pxofSave->CreateDataObject(TID_D3DRMFrame, SCENE_ROOT, NULL, 0, NULL, &pRootFrameObject); if (FAILED(hr)) { cout << "AddScene(): Could not create pRootFrameObject." << endl; goto e_Exit; } // next create the SCENE_ROOT FrameTransformMatrix as the Identity hr = pxofSave->CreateDataObject(TID_D3DRMFrameTransformMatrix, NULL, NULL, 16 * sizeof(float), rgfIdentity, &pRootTransformObject); if (FAILED(hr)) { cout << "AddScene(): Could not create pRootTransformObject." << endl; goto e_Exit; } hr = pRootFrameObject->AddDataObject(pRootTransformObject); if (FAILED(hr)) { cout << "AddScene(): Could not add pRootTransformObject to pRootFrameObject." << endl; goto e_Exit; } cout << endl << "Loading scene..." << endl; for (; !itDag.isDone() && itDag.depth() <= 1; itDag.next()) { MDagPath dagPath; stat = itDag.getPath(dagPath); if (!stat) { cout << "WARNING: Ignoring shape because could not get path to shape." << endl; continue; } #ifdef NO_INTERMEDIATE_OBJECTS if (MFnDagNode(dagPath).isIntermediateObject()) continue; #endif hr = AddShape(dagPath, true, pRootFrameObject, pxofSave); if (FAILED(hr)) cout << "AddScene(): Could not add top level shape." << endl; } cout.flush(); cout << "Writing scene data..."; cout.flush(); hr = pxofSave->SaveData(pRootFrameObject); if (FAILED(hr)) { cout << "AddScene(): Could not save scene data to file." << endl; goto e_Exit; } cout << "DONE!" << endl; cout.flush(); if (!g_bExportAnimation) goto e_Exit; hr = pxofSave->CreateDataObject(TID_D3DRMAnimationSet, NULL, NULL, 0, NULL, &pAnimSetObject); if (FAILED(hr)) { cout << "AddScene(): Could not create pAnimSetObject." << endl; goto e_Exit; } cout << "Loading animations..." << endl; cout.flush(); if (g_bKeyframeAnimation) hr = LoadKeyframedAnims(&nAnims, &aAnims); else hr = LoadFixedStepAnims(&nAnims, &aAnims); if (FAILED(hr)) { cout << "AddScene(): Could not save animations." << endl; goto e_Exit; } cout << "\t" << nAnims << " anim(s) loaded." << endl; cout << "Writing animations..."; cout.flush(); for (iAnim = 0; iAnim < nAnims; iAnim++) { hr = AddAnim(aAnims + iAnim, pAnimSetObject, pxofSave); if (FAILED(hr)) { cout << "AddScene(): Could not add an animation." << endl; continue; } } cout << "DONE!" << endl; cout.flush(); hr = pxofSave->SaveData(pAnimSetObject); if (FAILED(hr)) { cout << "AddScene(): Could not save animation data to file." << endl; goto e_Exit; } e_Exit: // turn on previously visible objects for (iPath = 0; iPath < VisiblePaths.length(); iPath++) { MPlug Visibility; MFnDagNode fnDag; stat = fnDag.setObject(VisiblePaths[iPath]); if (!stat) continue; Visibility = fnDag.findPlug("visibility", &stat); if (!stat) continue; stat = Visibility.setValue(true); if (!stat) continue; } if (pRootFrameObject) pRootFrameObject->Release(); if (pRootTransformObject) pRootTransformObject->Release(); if (pAnimSetObject) pAnimSetObject->Release(); if (pxofSave) pxofSave->Release(); if (pxofApi) pxofApi->Release(); cout << endl; if (FAILED(hr)) cout << "There were errors."; else cout << "Completed successfully."; cout << endl << endl << "REMARKS:" << endl; #ifdef ALWAYS_TESSELATE_NURBS cout << " - NURBS surfaces are currently always tesselated." << endl; #endif if (!g_bExportPatches) cout << " - When a NURBS surface is tesselated, it loses its material and skinning info." << endl; if (!itIkEffectors.isDone()) { cout << " - IkEffector nodes were found. This will probably affect skinning info." << endl; cout << " Please delete or disable IK nodes and retry for better results." << endl; } cout << " - PolyFaceSmoothing AFTER skinning will lead to bad results." << endl; cout << " (since the vertices added as a result of PolyFaceSmoothing will have no skin-weights.)" << endl; cout << "...................................................." << endl << endl; cout.flush(); delete[] aAnims; return hr; } void ParseOptions ( MString msOptions ) { if (msOptions.length() > 0) { MStringArray optionList; msOptions.split(';', optionList); // break out all the options. for (int iOption = 0; iOption < (int)optionList.length(); iOption++) { MStringArray theOption; optionList[iOption].split('=', theOption); if (theOption.length() > 1) { if (theOption[0] == "fileFormat") { if (theOption[1] == "binary") { g_FileFormat = DXFILEFORMAT_BINARY; } else if (theOption[1] == "compressed") { g_FileFormat = DXFILEFORMAT_COMPRESSED; } else // "text" { g_FileFormat = DXFILEFORMAT_TEXT; } } else if (theOption[0] == "exportAnimation") { if (theOption[1] == "false") { g_bExportAnimation = false; } else // "true" { g_bExportAnimation = true; } } else if (theOption[0] == "keyframeAnimation") { if (theOption[1] == "false") { g_bKeyframeAnimation = false; } else // "true" { g_bKeyframeAnimation = true; } } else if (theOption[0] == "animateEverything") { if (theOption[1] == "false") { g_bAnimateEverything = false; } else // "true" { g_bAnimateEverything = true; } } else if (theOption[0] == "frameStep") { g_iFrameStep = theOption[1].asInt(); } else if (theOption[0] == "flipU") { if (theOption[1] == "true") { g_iFlipU = -1; } else // "false" { g_iFlipU = 1; } } else if (theOption[0] == "flipV") { if (theOption[1] == "true") { g_iFlipV = 1; } else // "false" { g_iFlipV = -1; } } else if (theOption[0] == "exportPatches") { if (theOption[1] == "true") { g_bExportPatches = true; } else // "false" { g_bExportPatches = false; } } else if (theOption[0] == "relTexFilename") { if (theOption[1] == "true") { g_bRelativeTexFile = true; } else // "false" { g_bRelativeTexFile = false; } } } } } } MStatus xfileTranslator::writer ( // parameters const MFileObject& file, const MString& sOptions, MPxFileTranslator::FileAccessMode mode ) { g_Arrays.Initialize(); ParseOptions(sOptions); MString sFile = file.fullName(); int iExt = sFile.rindex('.'); MString sExt = sFile.substring(iExt, sFile.length() - 1); sFile = (sExt == ".x" || sExt == ".X") ? sFile : (sFile + ".x"); g_AddedPaths.clear(); AddScene(sFile.asChar()); g_Arrays.CleanUp(); g_iCount = 0; return MS::kSuccess; } MStatus initializePlugin ( MObject obj ) { MStatus stat = MS::kSuccess; char version[256]; strcpy(version, "0.3"); // plug-in version strcat(version, "."); strcat(version, DtAPIVersion()); MFnPlugin plugin(obj, "XFile Translator for Maya", version, "Any"); // register the translator stat = plugin.registerFileTranslator("XFile", "xfileTranslator.rgb", xfileTranslator::creator, "xfileTranslatorOpts", "", true); if (!stat) stat.perror("registerFileTranslator"); return stat; } MStatus uninitializePlugin ( MObject obj ) { MStatus status; MFnPlugin plugin(obj); status = plugin.deregisterFileTranslator("XFile"); if (!status) status.perror("deregisterFileTranslator"); return status; } /* HRESULT LoadFixedStepAnims ( SAnim* rgAnims ) { HRESULT hr = S_OK; cout << "\treading at intervals of " << g_iFrameStep << " frame(s)" << endl; // calculate the frames per second int iFPS = 1; switch(MTime::uiUnit()) { case MTime::kSeconds: // 1 fps iFPS = 1; break; case MTime::kMilliseconds: // 1000 fps iFPS = 1000; break; case MTime::kGames: // 15 fps iFPS = 15; break; case MTime::kFilm: // 24 fps iFPS = 24; break; case MTime::kPALFrame: // 25 fps iFPS = 25; break; case MTime::kNTSCFrame: // 30 fps iFPS = 30; break; case MTime::kShowScan: // 48 fps iFPS = 48; break; case MTime::kPALField: // 50 fps iFPS = 50; break; case MTime::kNTSCField: // 60 fps iFPS = 60; break; default: iFPS = 1; break; }; float fTimeFactor = 3600.0f / (float)iFPS; MTime timeStart(MAnimControl::minTime().value(), MTime::uiUnit()); MTime timeEnd(MAnimControl::maxTime().value(), MTime::uiUnit()); MTime timeCurrent(MAnimControl::currentTime().value(), MTime::uiUnit()); DtFrameSetStart((int)timeStart.value()); DtFrameSetEnd((int)timeEnd.value()); int cShapes = DtShapeGetCount(); MIntArray* rgrgiKeys = new MIntArray[cShapes]; for (int iShape = 0; iShape < cShapes; iShape++) { rgAnims[iShape].m_szName = new char[256]; rgAnims[iShape].m_nKeys = 0; rgAnims[iShape].m_aKeys = new SKey[1 + (DtFrameGetEnd() - DtFrameGetStart() + 1) / g_iFrameStep]; g_Arrays.Add(STRING, rgAnims[iShape].m_szName); char* szName; DtShapeGetName(iShape, &szName); strcpy(rgAnims[iShape].m_szName, szName); DtShapeGetTRSAnimKeys(iShape, &rgrgiKeys[iShape]); } for (int iFrame = DtFrameGetStart(); iFrame <= DtFrameGetEnd(); iFrame += g_iFrameStep) { DtFrameSet(iFrame); for (int iShape = 0; iShape < cShapes; iShape++) { if (rgrgiKeys[iShape].length() > 0 || g_bAnimateEverything) { rgAnims[iShape].m_aKeys[rgAnims[iShape].m_nKeys].m_iFrame = (int)((float)iFrame * fTimeFactor); float* rgfTRS; DtShapeGetMatrix(iShape, &rgfTRS); DtMatrixGetTransforms(rgfTRS, rgAnims[iShape].m_aKeys[rgAnims[iShape].m_nKeys].m_afPosition, rgAnims[iShape].m_aKeys[rgAnims[iShape].m_nKeys].m_afScale, rgAnims[iShape].m_aKeys[rgAnims[iShape].m_nKeys].m_afQuaternion, rgAnims[iShape].m_aKeys[rgAnims[iShape].m_nKeys].m_afRotation); rgAnims[iShape].m_nKeys++; } } } DtFrameSet((int)timeCurrent.value()); delete[] rgrgiKeys; return hr; } */ /*** TOOLS // print out the attributes of a dependency node for (unsigned iAttr = 0; iAttr < fnNode.attributeCount(); iAttr++) { MFnAttribute fnAttr(fnNode.attribute(iAttr)); cout << fnNode.name() << "." << fnAttr.name() << ": " << fnNode.attribute(iAttr).apiTypeStr() << endl; } // print the node containing the corresponging plug to 'plug' MPlugArray rgPlugs; plug.connectedTo(rgPlugs, true, true); for (int i = 0; i < (int)rgPlugs.length(); i++) { MFnDependencyNode fnNode(rgPlugs[i].node()); cout << fnNode.name() << "\t" << rgPlugs[i].name() << endl; } ***/